diff options
105 files changed, 4684 insertions, 4101 deletions
@@ -4,6 +4,8 @@ For a more comprehensive changelog of the latest experimental code, see: 1.8.0 (????-??-??) New Games: - Added support for Sfinx. + - Added support for Zork Nemesis: The Forbidden Lands. + - Added support for Zork: Grand Inquisitor. General: - Updated Munt MT-32 emulation code to version 1.5.0. @@ -15,6 +17,10 @@ For a more comprehensive changelog of the latest experimental code, see: head scene (bug #6728). It may have been happening in other scenes as well. +SCUMM: + - It is now possible to play Maniac Mansion from within Day of the + Tentacle, with a few caveats. See README for details. + 1.7.0 (2014-07-21) New Games: - Added support for Chivalry is Not Dead. @@ -15,26 +15,27 @@ Table of Contents: * 2.1 Reporting Bugs 3.0) Supported Games * 3.1 Copy Protection - * 3.2 Commodore64 games notes - * 3.3 Maniac Mansion NES notes - * 3.4 Macintosh games notes - * 3.5 Multi-CD games notes - * 3.6 The Curse of Monkey Island notes - * 3.7 Broken Sword games notes - * 3.8 Beneath a Steel Sky notes - * 3.9 Flight of the Amazon Queen notes - * 3.10 Gobliiins notes - * 3.11 Inherit the Earth: Quest for the Orb notes - * 3.12 Simon the Sorcerer notes - * 3.13 The Feeble Files notes - * 3.14 The Legend of Kyrandia notes - * 3.15 Sierra AGI games Predictive Input Dialog notes - * 3.16 Mickey's Space Adventure notes - * 3.17 Winnie the Pooh notes - * 3.18 Troll's Tale notes - * 3.19 Dragon History notes - * 3.20 Simultaneous speech and subtitles in Sierra SCI games - * 3.21 Known Problems + * 3.2 Day of the Tentacle notes + * 3.3 Commodore64 games notes + * 3.4 Maniac Mansion NES notes + * 3.5 Macintosh games notes + * 3.6 Multi-CD games notes + * 3.7 The Curse of Monkey Island notes + * 3.8 Broken Sword games notes + * 3.9 Beneath a Steel Sky notes + * 3.10 Flight of the Amazon Queen notes + * 3.11 Gobliiins notes + * 3.12 Inherit the Earth: Quest for the Orb notes + * 3.13 Simon the Sorcerer notes + * 3.14 The Feeble Files notes + * 3.15 The Legend of Kyrandia notes + * 3.16 Sierra AGI games Predictive Input Dialog notes + * 3.17 Mickey's Space Adventure notes + * 3.18 Winnie the Pooh notes + * 3.19 Troll's Tale notes + * 3.20 Dragon History notes + * 3.21 Simultaneous speech and subtitles in Sierra SCI games + * 3.22 Known Problems 4.0) Supported Platforms 5.0) Running ScummVM * 5.1 Command Line Options @@ -200,7 +201,7 @@ SCUMM Games by LucasArts: The Dig [dig] The Curse of Monkey Island [comi] -AGI Games by Sierra: +AGI and preAGI Games by Sierra: The Black Cauldron [bc] Gold Rush! [goldrush] King's Quest I [kq1] @@ -217,6 +218,9 @@ AGI Games by Sierra: Space Quest I: The Sarien Encounter [sq1] Space Quest II: Vohaul's Revenge [sq2] Fanmade Games [agi-fanmade] + Mickey's Space Adventure [mickey] + Troll's Tale [troll] + Winnie the Pooh in the Hundred Acre Wood [winnie] AGOS Games by Adventuresoft/Horrorsoft: Elvira - Mistress of the Dark [elvira1] @@ -235,6 +239,13 @@ AGOS Games by Adventuresoft/Horrorsoft: - Swampy Adventures [swampy] The Feeble Files [feeble] +Composer Games by Animation Magic: + Darby the Dragon [darby] + Gregory and the Hot Air Balloon [gregory] + Magic Tales: Liam Finds a Story [liam] + The Princess and the Crab [princess] + Sleeping Cub's Test of Courage [sleepingcub] + GOB Games by Coktel Vision: Bambou le sauveur de la jungle [bambou] Bargon Attack [bargon] @@ -250,6 +261,22 @@ GOB Games by Coktel Vision: Urban Runner [urban] Ween: The Prophecy [ween] +Living Books Games: + Aesop's Fables: The Tortoise and the Hare [tortoise] + Arthur's Birthday [arthurbday] + Arthur's Teacher Trouble [arthur] + Dr. Seuss's ABC [seussabc] + Green Eggs and Ham [greeneggs] + Harry and the Haunted House [harryhh] + Just Grandma and Me [grandma] + Little Monster at School [lilmonster] + Ruff's Bone [ruff] + Sheila Rae, the Brave [sheila] + Stellaluna [stellaluna] + The Berenstain Bears Get in a Fight [bearfight] + The Berenstain Bears in the Dark [beardark] + The New Kid on the Block [newkid] + MADE Games by Activision: Leather Goddesses of Phobos 2 [lgop2] Return to Zork [rtz] @@ -257,34 +284,106 @@ MADE Games by Activision: The Manhole [manhole] Other Games: + 3 Skulls of the Toltecs [toltecs] + Blue Force [blueforce] Beneath a Steel Sky [sky] Broken Sword: The Shadow of the Templars [sword1] Broken Sword II: The Smoking Mirror [sword2] + Bud Tucker in Double Trouble [tucker] Cruise for a Corpse [cruise] Discworld [dw] Discworld 2: Missing Presumed ...!? [dw2] Dragon History [draci] Drascula: The Vampire Strikes Back [drascula] + DreamWeb [dreamweb] Eye of the Beholder [eob] Eye of the Beholder II: The Legend of Darkmoon [eob2] Flight of the Amazon Queen [queen] Future Wars [fw] + Hopkins FBI [hopkins] + Hugo's House of Horrors [hugo1] + Hugo 2: Whodunit? [hugo2] + Hugo 3: Jungle of Doom [hugo3] + I Have No Mouth, and I Must Scream [ihnm] Inherit the Earth: Quest for the Orb [ite] Nippon Safes Inc. [nippon] Lands of Lore: The Throne of Chaos [lol] + Lure of the Temptress [lure] + Mortville Manor [mortevielle] + Nippon Safes Inc. [nippon] + Ringworld: Revenge Of The Patriarch [ringworld] + Return to Ringworld [ringworld2] + Sfinx [sfinx] + Soltys [soltys] + TeenAgent [teenagent] The Journeyman Project: Pegasus Prime [pegasus] The Legend of Kyrandia [kyra1] The Legend of Kyrandia: The Hand of Fate [kyra2] The Legend of Kyrandia: Malcolm's Revenge [kyra3] + The 7th Guest [t7g] + The Neverhood [neverhood] + Tony Tough and the Night of Roasted Moths [tony] + Toonstruck [toon] Touche: The Adventures of the Fifth Musketeer [touche] + Voyeur [voyeur] + +SCI Games by Sierra Entertainment: + Castle of Dr. Brain [castlebrain] + Codename: ICEMAN [iceman] + Conquests of Camelot [camelot] + Conquests of the Longbow [longbow] + EcoQuest: The Search for Cetus [ecoquest] + EcoQuest 2: Lost Secret of the Rainforest [ecoquest2] + Freddy Pharkas: Frontier Pharmacist [freddypharkas] + Hoyle's Book of Games 1 [hoyle1] + Hoyle's Book of Games 2 [hoyle2] + Hoyle's Book of Games 3 [hoyle3] + Hoyle Classic Card Games [hoyle4] + Jones in the Fast Lane [jones] + King's Quest I [kq1sci] + King's Quest IV [kq4sci] + King's Quest V [kq5] + King's Quest VI [kq6] + Laura Bow: The Colonel's Bequest [laurabow] + Laura Bow 2: The Dagger of Amon Ra [laurabow2] + Leisure Suit Larry 1 [lsl1sci] + Leisure Suit Larry 2 [lsl2] + Leisure Suit Larry 3 [lsl3] + Leisure Suit Larry 5 [lsl5] + Leisure Suit Larry 6 [lsl6] + Mixed-up Fairy Tales [fairytales] + Mixed-up Mother Goose [mothergoose] + Pepper's Adventures in Time [pepper] + Police Quest 1 [pq1sci] + Police Quest 2 [pq2] + Police Quest 3 [pq3] + Quest for Glory 1/Hero's Quest [qfg1] + Quest for Glory 1 [qfg1vga] + Quest for Glory 2 [qfg2] + Quest for Glory 3 [qfg3] + Slater & Charlie Go Camping [slater] + Space Quest I [sq1sci] + Space Quest III [sq3] + Space Quest IV [sq4] + Space Quest V [sq5] + The Island of Dr. Brain [islandbrain] + +Wintermute Games: + Chivalry is Not Dead [chivalry] + +ZVISION Games by Activision: + Zork Nemesis: The Forbidden Lands [znemesis] + Zork: Grand Inquisitor [zgi] SCUMM Games by Humongous Entertainment: Backyard Baseball [baseball] Backyard Baseball 2001 [baseball2001] Backyard Baseball 2003 [baseball2003] Backyard Football [football] + Backyard Football 2002 [football2002] + Bear Stormin' [brstorm] Big Thinkers First Grade [thinker1] Big Thinkers Kindergarten [thinkerk] Blue's 123 Time Activities [Blues123Time] @@ -309,6 +408,7 @@ SCUMM Games by Humongous Entertainment: Let's Explore the Airport with Buzzy [airport] Let's Explore the Farm with Buzzy [farm] Let's Explore the Jungle with Buzzy [jungle] + Pajama Sam: Games to Play on Any Day [pjgames] Pajama Sam 1: No Need to Hide When It's Dark Outside [pajama] Pajama Sam 2: Thunder and Lightning @@ -333,38 +433,20 @@ SCUMM Games by Humongous Entertainment: SPY Fox in Cheese Chase [chase] SPY Fox in Hold the Mustard [mustard] -Living Books Games: - Aesop's Fables: The Tortoise and the Hare [tortoise] - Arthur's Birthday [arthurbday] - Arthur's Teacher Trouble [arthur] - Dr. Seuss's ABC [seussabc] - Green Eggs and Ham [greeneggs] - Harry and the Haunted House [harryhh] - Just Grandma and Me [grandma] - Little Monster at School [lilmonster] - Ruff's Bone [ruff] - Sheila Rae, the Brave [sheila] - Stellaluna [stellaluna] - The Berenstain Bears Get in a Fight [bearfight] - The Berenstain Bears in the Dark [beardark] - The New Kid on the Block [newkid] - The following games should load, but are not yet fully playable. Play these at your own risk, and please do not file bug reports about them. If you want the latest updates on game compatibility, visit our web site and view the compatibility chart. - Backyard Football 2002 [football2002] Backyard Soccer [soccer] Backyard Soccer MLS [soccermls] Backyard Soccer 2004 [soccer2004] Blue's Treasure Hunt [BluesTreasureHunt] - Pajama Sam: Games to Play on Any Day [pjgames] The following games are based on the SCUMM engine, but NOT supported by ScummVM (yet): - Other Humongous Entertainment games + Moonbase Commander Please be aware that the engines may contain bugs and unimplemented features that sometimes make it impossible to finish the game. Save @@ -410,7 +492,29 @@ ScummVM will skip copy protection in the following games: * Zak McKracken and the Alien Mindbenders -3.2) Commodore64 games notes: +3.2) Day of the Tentacle notes: +---- -------------------------- + +At one point in the game, you come across a computer that allows you +to play the original Maniac Mansion as an easter egg. ScummVM supports +this, with a few caveats: + +ScummVM will scan your configuration file for a game that's in a +'Maniac' sub-folder of your Day of the Tentacle folder. If you've +copied the data files from the CD version, this should already be the +case but you have to add the game to ScummVM as well. + +To return to Day of the Tentacle, press F5 and select "Return to +Launcher". + +This means that you could in theory use any game as the easter egg. +Indeed, there is a "secret" configuration setting, "easter_egg", to +override the ID of the game to run. Be aware, though, that not all +games support returning to the launcher, and setting it up to use Day +of the Tentacle itself as the easter egg game is not recommended. + + +3.3) Commodore64 games notes: ---- ------------------------ Both Maniac Mansion and Zak McKracken run but Maniac Mansion is not yet playable. Simply name the D64 disks "maniac1.d64" and "maniac2.d64" @@ -424,7 +528,7 @@ to Commodore64. We recommend using the much simpler approach described in the previous paragraph. -3.3) Maniac Mansion NES notes: +3.4) Maniac Mansion NES notes: ---- ------------------------- Supported versions are English GB (E), French (F), German (G), Italian (I), Swedish (SW) and English US (U). ScummVM requires just the PRG section @@ -453,7 +557,7 @@ section. To do so use the 'extract_mm_nes' utility from the tools package. -3.4) Macintosh games notes: +3.5) Macintosh games notes: ---- ---------------------- All LucasArts SCUMM based adventures, except COMI, also exist in versions for the Macintosh. ScummVM can use most (all?) of them, however, in some @@ -481,7 +585,7 @@ disk see: http://wiki.scummvm.org/index.php/HOWTO-Mac_Games -3.5) Multi-CD games notes: +3.6) Multi-CD games notes: ---- --------------------- In general, ScummVM does not deal very well with Multi-CD games. This is because ScummVM assumes everything about a game can be found in one @@ -496,7 +600,7 @@ files. Usually, when a file appears on more than one CD you can pick either of them. -3.6) The Curse of Monkey Island notes: +3.7) The Curse of Monkey Island notes: ---- --------------------------------- For this game, you will need the comi.la0, comi.la1 and comi.la2 files. The comi.la0 file can be found on either CD, but since they are @@ -508,7 +612,7 @@ two CDs. Some of the files appear on both CDs, but again they're identical. -3.7) Broken Sword games notes: +3.8) Broken Sword games notes: ---- ------------------------- The instructions for the Broken Sword games are for the Sold-Out Software versions, with each game on two CDs, since these were the @@ -517,7 +621,7 @@ them. Hopefully they are general enough to be useful to other releases as well. -3.7.1) Broken Sword games cutscenes: +3.8.1) Broken Sword games cutscenes: ------ ----------------------------- The cutscenes for the Broken Sword games have a bit of a history (see the next section, if you are interested), but in general all you need to @@ -558,7 +662,7 @@ currently does not work when running PlayStation videos. (Broken Sword II already has subtitles; no extra work is needed for them.) -3.7.2) Broken Sword games cutscenes, in retrospect: +3.8.2) Broken Sword games cutscenes, in retrospect: ------ -------------------------------------------- The original releases of the Broken Sword games used RAD Game Tools's Smacker(tm) format. As RAD was unwilling to open the older legacy @@ -583,7 +687,7 @@ decoding MPEG movies added a lot of complexity, and they didn't look as good as the Smacker and DXA versions anyway. -3.7.3) Broken Sword: +3.8.3) Broken Sword: ------ ------------- For this game, you will need all of the files from the clusters directories on both CDs. For the Windows and Macintosh versions, you @@ -600,7 +704,7 @@ makes little difference. The PlayStation version requires tunes.dat and tunes.tab. -3.7.4) Broken Sword II: +3.8.4) Broken Sword II: ------ ---------------- For this game, you will need all of the files from the clusters directories on both CDs. (Actually, a few of them may not be strictly @@ -615,7 +719,7 @@ In addition, you will need the cd.inf and, optionally, the startup.inf files from the sword2 directory on CD 1. -3.8) Beneath a Steel Sky notes: +3.9) Beneath a Steel Sky notes: ---- -------------------------- Starting with ScummVM 0.8.0 you need the additional 'SKY.CPT' file to run Beneath a Steel Sky. @@ -626,7 +730,7 @@ files (SKY.DNR, SKY.DSK), in your extrapath, or in the directory where your ScummVM executable resides. -3.9) Flight of the Amazon Queen notes: +3.10) Flight of the Amazon Queen notes: ---- --------------------------------- In order to use a non-freeware version of Flight of the Amazon Queen (from original CD), you will need to place the 'queen.tbl' file @@ -641,7 +745,7 @@ specific version, and thus removing the run-time dependency on the sound effects with MP3, OGG or FLAC. -3.10) Gobliiins notes: +3.11) Gobliiins notes: ----- ---------------- The CD versions of the Gobliiins series contain one big audio track which you need to rip (see the section on using compressed audio files) @@ -651,7 +755,7 @@ track and its volume is therefore changed with the music volume control as well. -3.11) Inherit the Earth: Quest for the Orb notes: +3.12) Inherit the Earth: Quest for the Orb notes: ----- ------------------------------------------- In order to run the Mac OS X Wyrmkeep re-release of the game you will need to copy over data from the CD to your hard disk. If you're on a PC @@ -671,14 +775,14 @@ format, as they should include both resource and data forks. Copy all 'ITE *' files. -3.12) Simon the Sorcerer 1 and 2 notes: +3.13) Simon the Sorcerer 1 and 2 notes: ----- --------------------------------- If you have the dual version of Simon the Sorcerer 1 or 2 on CD, you will find the Windows version in the main directory of the CD and the DOS version in the DOS directory of the CD. -3.13) The Feeble Files notes: +3.14) The Feeble Files notes: ----- ----------------------- If you have the Windows version of The Feeble Files, there are several things to note. @@ -696,7 +800,7 @@ Rename voices.wav on CD3 to voices3.wav Rename voices.wav on CD4 to voices4.wav -3.14) The Legend of Kyrandia notes: +3.15) The Legend of Kyrandia notes: ----- ----------------------------- To run The Legend of Kyrandia under ScummVM you need the 'kyra.dat' file. The file should always be included in official ScummVM packages. @@ -707,7 +811,7 @@ thus you only need to grab it in case ScummVM complains about the file being missing. -3.15) Sierra AGI games Predictive Input Dialog notes: +3.16) Sierra AGI games Predictive Input Dialog notes: ----- ----------------------------------------------- The Predictive Input Dialog is a ScummVM aid for running AGI engine games (which notoriously require command line input) on devices with @@ -761,7 +865,7 @@ naturally mapping the functionality to the numeric keypad. Also, the dialog's buttons can be navigated with the arrow and the enter keys. -3.16) Mickey's Space Adventure notes: +3.17) Mickey's Space Adventure notes: ----- ------------------------------- To run Mickey's Space Adventure under ScummVM, the original executable of the game (mickey.exe) is needed together with the game's data files. @@ -776,7 +880,7 @@ game's screen to change location, similar to many adventure games, which is simpler and more straightforward than moving around using the menu. -3.17) Winnie the Pooh notes: +3.18) Winnie the Pooh notes: ----- ---------------------- It is possible to import saved games from the original interpreter of the game into ScummVM. @@ -791,14 +895,14 @@ game's screen to change location, similar to many adventure games, which is simpler and more straightforward than moving around using the menu. -3.18) Troll's Tale notes: +3.19) Troll's Tale notes: ----- ------------------- The original game came in a PC booter disk, therefore it is necessary to dump the contents of that disk in an image file and name it "troll.img" to be able to play the game under ScummVM. -3.19) Dragon History notes: +3.20) Dragon History notes: ----- --------------------- There are 4 language variants of the game: Czech, English, Polish and German. Each of them is distributed in a separate archive. The only @@ -816,7 +920,7 @@ All game files and the walkthrough can be downloaded from http://www.ucw.cz/draci-historie/index-en.html -3.20) Simultaneous speech and subtitles in Sierra SCI games: +3.21) Simultaneous speech and subtitles in Sierra SCI games: ----- ------------------------------------------------------ Certain CD versions of Sierra SCI games had both speech and text resources. Some have an option to toggle between the two, but there are @@ -864,7 +968,7 @@ Space Quest 4 CD: options dialog, or via ScummVM's audio options. -3.21) Known Problems: +3.22) Known Problems: ----- --------------- This release has the following known problems. There is no need to report them, although patches to fix them are welcome. If you discover a @@ -1244,6 +1348,7 @@ Engines which currently support returning to the Launcher are: TOUCHE TSAGE TUCKER + ZVISION 5.5) Hotkeys: @@ -1408,6 +1513,26 @@ other games. t - Switch between 'Voice only', 'Voice and Text' and 'Text only' + Zork: Grand Inquisitor + Ctrl-s - Save + Ctrl-r - Restore + Ctrl-q - Quit + Ctrl-p - Preferences + F1 - Help + F5 - Inventory + F6 - Spellbook + F7 - Score + F8 - Put away current object/forget spell + F9 - Extract coin (must have the coin bag) + Space - Skips movies + + Zork Nemesis: The Forbidden Lands + Ctrl-s - Save + Ctrl-r - Restore + Ctrl-q - Quit + Ctrl-p - Preferences + Space - Skips movies + Note that using Ctrl-f or Ctrl-g is not recommended: games can crash when being run faster than their normal speed, as scripts will lose synchronisation. @@ -1527,6 +1652,7 @@ Where 'xxx' is exact the saved game slot (ie 001) under ScummVM TOUCHE TSAGE TUCKER + ZVISION --save-slot/-x: @@ -1559,6 +1685,7 @@ Where 'xxx' is exact the saved game slot (ie 001) under ScummVM TOUCHE TSAGE TUCKER + ZVISION 7.0) Music and Sound: @@ -2240,6 +2367,27 @@ The 7th Guest adds the following non-standard keyword: normal speed, to avoid music synchronization issues +Zork Nemesis: The Forbidden Lands adds the following non-standard keywords: + + originalsaveload bool If true, the original save/load screens are + used instead of the enhanced ScummVM ones + doublefps bool If true, game FPS are increased from 30 to 60 + venusenabled bool If true, the in-game Venus help system is + enabled + noanimwhileturning bool If true, animations are disabled while turning + in panoramic mode + +Zork: Grand Inquisitor adds the following non-standard keywords: + + originalsaveload bool If true, the original save/load screens are + used instead of the enhanced ScummVM ones + doublefps bool If true, game FPS are increased from 30 to 60 + noanimwhileturning bool If true, animations are disabled while turning + in panoramic mode + mpegmovies bool If true, the hires MPEG movies are used in the + DVD version of the game, instead of the lowres + AVI ones + 8.2) Custom game options that can be toggled via the GUI ---- --------------------------------------------------- diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index c455c4ce2e..5821856c30 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -49,9 +49,9 @@ OpenGLGraphicsManager::OpenGLGraphicsManager() _displayWidth(0), _displayHeight(0), _defaultFormat(), _defaultFormatAlpha(), _gameScreen(nullptr), _gameScreenShakeOffset(0), _overlay(nullptr), _overlayVisible(false), _cursor(nullptr), - _cursorX(0), _cursorY(0), _cursorHotspotX(0), _cursorHotspotY(0), _cursorHotspotXScaled(0), - _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0), _cursorKeyColor(0), - _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false) + _cursorX(0), _cursorY(0), _cursorDisplayX(0),_cursorDisplayY(0), _cursorHotspotX(0), _cursorHotspotY(0), + _cursorHotspotXScaled(0), _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0), + _cursorKeyColor(0), _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false) #ifdef USE_OSD , _osdAlpha(0), _osdFadeStartTime(0), _osd(nullptr) #endif @@ -351,7 +351,7 @@ void OpenGLGraphicsManager::updateScreen() { return; } - // Clear the screen buffer + // Clear the screen buffer. GLCALL(glClear(GL_COLOR_BUFFER_BIT)); const GLfloat shakeOffset = _gameScreenShakeOffset * (GLfloat)_displayHeight / _gameScreen->getHeight(); @@ -370,12 +370,42 @@ void OpenGLGraphicsManager::updateScreen() { // visible. const GLfloat cursorOffset = _overlayVisible ? 0 : shakeOffset; - _cursor->draw(_cursorX - _cursorHotspotXScaled, _cursorY - _cursorHotspotYScaled + cursorOffset, + _cursor->draw(_cursorDisplayX - _cursorHotspotXScaled, + _cursorDisplayY - _cursorHotspotYScaled + cursorOffset, _cursorWidthScaled, _cursorHeightScaled); } + // Fourth step: Draw black borders around the game screen when no overlay + // is visible. This makes sure that the mouse cursor etc. is only drawn + // in the actual game screen area in this case. + if (!_overlayVisible) { + GLCALL(glColor4f(0.0f, 0.0f, 0.0f, 1.0f)); + + GLCALL(glDisable(GL_TEXTURE_2D)); + GLCALL(glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + + // Top border. + drawRect(0, 0, _outputScreenWidth, _displayY); + + // Left border. + drawRect(0, 0, _displayX, _outputScreenHeight); + + // Bottom border. + const int y = _displayY + _displayHeight; + drawRect(0, y, _outputScreenWidth, _outputScreenHeight - y); + + // Right border. + const int x = _displayX + _displayWidth; + drawRect(x, 0, _outputScreenWidth - x, _outputScreenHeight); + + GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + GLCALL(glEnable(GL_TEXTURE_2D)); + + GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f)); + } + #ifdef USE_OSD - // Fourth step: Draw the OSD. + // Fifth step: Draw the OSD. if (_osdAlpha > 0) { Common::StackLock lock(_osdMutex); @@ -435,10 +465,16 @@ int16 OpenGLGraphicsManager::getOverlayHeight() { void OpenGLGraphicsManager::showOverlay() { _overlayVisible = true; + + // Update cursor position. + setMousePosition(_cursorX, _cursorY); } void OpenGLGraphicsManager::hideOverlay() { _overlayVisible = false; + + // Update cursor position. + setMousePosition(_cursorX, _cursorY); } Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const { @@ -892,8 +928,8 @@ void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) { const int16 width = _gameScreen->getWidth(); const int16 height = _gameScreen->getHeight(); - x = (x * width) / _displayWidth; - y = (y * height) / _displayHeight; + x = (x * width) / (int)_displayWidth; + y = (y * height) / (int)_displayHeight; // Make sure we only supply valid coordinates. x = CLIP<int16>(x, 0, width - 1); @@ -901,6 +937,19 @@ void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) { } } +void OpenGLGraphicsManager::setMousePosition(int x, int y) { + _cursorX = x; + _cursorY = y; + + if (_overlayVisible) { + _cursorDisplayX = x; + _cursorDisplayY = y; + } else { + _cursorDisplayX = CLIP<int>(x, _displayX, _displayX + _displayWidth - 1); + _cursorDisplayY = CLIP<int>(y, _displayY, _displayY + _displayHeight - 1); + } +} + Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &format, bool wantAlpha) { GLenum glIntFormat, glFormat, glType; if (format.bytesPerPixel == 1) { @@ -1046,6 +1095,9 @@ void OpenGLGraphicsManager::recalculateDisplayArea() { // We center the screen in the middle for now. _displayX = (_outputScreenWidth - _displayWidth ) / 2; _displayY = (_outputScreenHeight - _displayHeight) / 2; + + // Update the cursor position to adjust for new display area. + setMousePosition(_cursorX, _cursorY); } void OpenGLGraphicsManager::updateCursorPalette() { @@ -1163,4 +1215,20 @@ void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const delete[] pixels; } +void OpenGLGraphicsManager::drawRect(GLfloat x, GLfloat y, GLfloat w, GLfloat h) { + if (w < 0 || h < 0) { + return; + } + + const GLfloat vertices[4*2] = { + x, y, + x + w, y, + x, y + h, + x + w, y + h + }; + GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices)); + + GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +} + } // End of namespace OpenGL diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h index dde21533b0..cec970e0cc 100644 --- a/backends/graphics/opengl/opengl-graphics.h +++ b/backends/graphics/opengl/opengl-graphics.h @@ -155,7 +155,7 @@ protected: * @param x X coordinate in physical coordinates. * @param y Y coordinate in physical coordinates. */ - void setMousePosition(int x, int y) { _cursorX = x; _cursorY = y; } + void setMousePosition(int x, int y); /** * Query the mouse position in physical coordinates. @@ -394,6 +394,16 @@ private: int _cursorY; /** + * X coordinate used for drawing the cursor. + */ + int _cursorDisplayX; + + /** + * Y coordinate used for drawing the cursor. + */ + int _cursorDisplayY; + + /** * The X offset for the cursor hotspot in unscaled coordinates. */ int _cursorHotspotX; @@ -454,6 +464,11 @@ private: */ byte _cursorPalette[3 * 256]; + /** + * Draws a rectangle + */ + void drawRect(GLfloat x, GLfloat y, GLfloat w, GLfloat h); + #ifdef USE_OSD // // OSD diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index 3ff1b939ef..798772cc24 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -396,12 +396,6 @@ void OSystem_Android::initBackend() { EventsBaseBackend::initBackend(); } -void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const { - ENTER(); - - JNI::getPluginDirectories(dirs); -} - bool OSystem_Android::hasFeature(Feature f) { return (f == kFeatureFullscreenMode || f == kFeatureAspectRatioCorrection || @@ -600,10 +594,4 @@ Common::String OSystem_Android::getSystemProperty(const char *name) const { return Common::String(value, len); } -#ifdef DYNAMIC_MODULES -void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const { - ((OSystem_Android *)g_system)->addPluginDirectories(dirs); -} -#endif - #endif diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h index 28016f5e3e..ade84dd42d 100644 --- a/backends/platform/android/android.h +++ b/backends/platform/android/android.h @@ -96,13 +96,6 @@ extern void checkGlError(const char *expr, const char *file, int line); #define GLTHREADCHECK do { } while (false) #endif -#ifdef DYNAMIC_MODULES -class AndroidPluginProvider : public POSIXPluginProvider { -protected: - virtual void addCustomDirectories(Common::FSList &dirs) const; -}; -#endif - class OSystem_Android : public EventsBaseBackend, public PaletteManager { private: // passed from the dark side @@ -177,7 +170,6 @@ public: virtual ~OSystem_Android(); virtual void initBackend(); - void addPluginDirectories(Common::FSList &dirs) const; void enableZoning(bool enable) { _enable_zoning = enable; } virtual bool hasFeature(Feature f); diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index 69aa9d8303..5be9f86770 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -2,183 +2,90 @@ # These must be incremented for each market upload ANDROID_VERSIONCODE = 6 -ANDROID_PLUGIN_VERSIONCODE = 6 -JAVA_FILES = \ - ScummVM.java \ - ScummVMEvents.java \ - ScummVMEventsHoneycomb.java \ - ScummVMApplication.java \ - ScummVMActivity.java \ - EditableSurfaceView.java \ - MouseHelper.java \ - Unpacker.java +ANDROID_TARGET_VERSION = 14 -JAVA_FILES_PLUGIN = \ - PluginProvider.java - -JAVA_FILES_GEN = \ - Manifest.java \ - R.java +NDK_BUILD = $(ANDROID_NDK)/ndk-build APP_ABI=$(ABI) +SDK_ANDROID = $(ANDROID_SDK)/tools/android PATH_DIST = $(srcdir)/dists/android PATH_RESOURCES = $(PATH_DIST)/res PORT_DISTFILES = $(PATH_DIST)/README.Android +DIST_JAVA_SRC_DIR = $(srcdir)/backends/platform/android/org RESOURCES = \ - $(PATH_RESOURCES)/values/strings.xml \ - $(PATH_RESOURCES)/values/margins.xml \ - $(PATH_RESOURCES)/values-television/margins.xml \ - $(PATH_RESOURCES)/layout/main.xml \ - $(PATH_RESOURCES)/layout/splash.xml \ - $(PATH_RESOURCES)/drawable/gradient.xml \ - $(PATH_RESOURCES)/drawable/scummvm.png \ - $(PATH_RESOURCES)/drawable/scummvm_big.png \ - $(PATH_RESOURCES)/drawable-xhdpi/ouya_icon.png - -PLUGIN_RESOURCES = \ - $(PATH_RESOURCES)/values/strings.xml \ - $(PATH_RESOURCES)/drawable/scummvm.png - -# FIXME: find/mark plugin entry points and add all this back again: -#LDFLAGS += -Wl,--gc-sections -#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden - -AAPT = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/aapt -ADB = $(ANDROID_SDK)/platform-tools/adb -DX = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/dx -APKBUILDER = java -Xmx128M -classpath $(ANDROID_SDK)/tools/lib/sdklib.jar com.android.sdklib.build.ApkBuilderMain -JAVAC ?= javac -JAVACFLAGS = -source 1.5 -target 1.5 - -ANDROID_JAR = $(ANDROID_SDK)/platforms/android-14/android.jar + $(PATH_BUILD_RES)/values/strings.xml \ + $(PATH_BUILD_RES)/values-television/margins.xml \ + $(PATH_BUILD_RES)/layout/main.xml \ + $(PATH_BUILD_RES)/drawable/scummvm.png \ + $(PATH_BUILD_RES)/drawable/scummvm_big.png \ + $(PATH_BUILD_RES)/drawable-xhdpi/ouya_icon.png + +DIST_ANDROID_MK = $(PATH_DIST)/jni/Android.mk +DIST_BUILD_XML = $(PATH_DIST)/custom_rules.xml 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 - -PATH_STAGE_PREFIX = build.stage -PATH_STAGE_MAIN = $(PATH_STAGE_PREFIX).main - -PATH_REL = org/scummvm/scummvm -PATH_SRC_TOP = $(srcdir)/backends/platform/android -PATH_SRC = $(PATH_SRC_TOP)/$(PATH_REL) - -PATH_GEN_TOP = $(PATH_BUILD)/java -PATH_GEN = $(PATH_GEN_TOP)/$(PATH_REL) -PATH_CLASSES_MAIN = $(PATH_BUILD_CLASSES_MAIN_TOP)/$(PATH_REL) -PATH_CLASSES_PLUGIN = $(PATH_BUILD_CLASSES_PLUGIN_TOP)/$(PATH_REL) +PATH_BUILD_RES = $(PATH_BUILD)/res +PATH_BUILD_LIBSCUMMVM = $(PATH_BUILD)/lib/$(ABI)/libscummvm.so FILE_MANIFEST_SRC = $(srcdir)/dists/android/AndroidManifest.xml FILE_MANIFEST = $(PATH_BUILD)/AndroidManifest.xml -FILE_DEX = $(PATH_BUILD)/classes.dex -FILE_DEX_PLUGIN = $(PATH_BUILD)/plugins/classes.dex -FILE_RESOURCES = resources.ap_ -FILE_RESOURCES_MAIN = $(PATH_BUILD)/$(FILE_RESOURCES) - -SRC_GEN = $(addprefix $(PATH_GEN)/, $(JAVA_FILES_GEN)) -CLASSES_MAIN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES:%.java=%.class)) -CLASSES_GEN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES_GEN:%.java=%.class)) -CLASSES_PLUGIN = $(addprefix $(PATH_CLASSES_PLUGIN)/, $(JAVA_FILES_PLUGIN:%.java=%.class)) - -APK_MAIN = scummvm.apk +APK_MAIN = ScummVM-debug.apk +APK_MAIN_RELEASE = ScummVM-release-unsigned.apk APK_PLUGINS = $(patsubst plugins/lib%.so, scummvm-engine-%.apk, $(PLUGINS)) -$(FILE_MANIFEST): $(FILE_MANIFEST_SRC) +$(FILE_MANIFEST): $(FILE_MANIFEST_SRC) | $(PATH_BUILD) @$(MKDIR) -p $(@D) sed "s/@ANDROID_VERSIONCODE@/$(ANDROID_VERSIONCODE)/" < $< > $@ -$(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_JAR) - -$(PATH_CLASSES_MAIN)/%.class: $(PATH_GEN)/%.java $(SRC_GEN) +$(PATH_BUILD)/res/%: $(PATH_DIST)/res/% | $(PATH_BUILD) @$(MKDIR) -p $(@D) - $(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $< + $(CP) $< $@ -$(PATH_CLASSES_MAIN)/%.class: $(PATH_SRC)/%.java $(SRC_GEN) +$(PATH_BUILD)/libs/%: $(PATH_DIST)/libs/% | $(PATH_BUILD) @$(MKDIR) -p $(@D) - $(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP):$(PATH_GEN_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $< + $(CP) $< $@ -$(PATH_CLASSES_PLUGIN)/%.class: $(PATH_SRC)/%.java - @$(MKDIR) -p $(@D) - $(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_PLUGIN_TOP) -bootclasspath $(ANDROID_JAR) $< +$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_SHADERS) $(DIST_BUILD_XML) | $(PATH_BUILD) + $(INSTALL) -d $(PATH_BUILD_ASSETS) + $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(PATH_BUILD_ASSETS)/ + $(INSTALL) -d $(PATH_BUILD)/jni + $(INSTALL) -c -m 644 $(DIST_ANDROID_MK) $(PATH_BUILD)/jni + $(INSTALL) -c -m 644 $(DIST_BUILD_XML) $(PATH_BUILD) -$(FILE_DEX): $(CLASSES_MAIN) $(CLASSES_GEN) - $(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_MAIN_TOP) +$(PATH_BUILD): $(DIST_ANDROID_MK) + $(MKDIR) -p $(PATH_BUILD) $(PATH_BUILD)/res + $(MKDIR) -p $(PATH_BUILD)/libs -$(FILE_DEX_PLUGIN): $(CLASSES_PLUGIN) - @$(MKDIR) -p $(@D) - $(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_PLUGIN_TOP) +$(PATH_BUILD_LIBSCUMMVM): libscummvm.so | $(PATH_BUILD) + $(INSTALL) -c -m 644 libscummvm.so $(PATH_BUILD) + $(STRIP) $(PATH_BUILD)/libscummvm.so + cd $(PATH_BUILD); $(NDK_BUILD) -$(PATH_BUILD)/%/AndroidManifest.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml - @$(MKDIR) -p $(@D) - $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-manifest.xml $(ANDROID_PLUGIN_VERSIONCODE) $@ +$(PATH_BUILD_RES): $(RESOURCES) | $(PATH_BUILD) -$(PATH_STAGE_PREFIX).%/res/values/strings.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml - @$(MKDIR) -p $(@D) - $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-strings.xml $(ANDROID_PLUGIN_VERSIONCODE) $@ +setupapk: $(FILE_MANIFEST) $(PATH_BUILD_RES) $(PATH_BUILD_ASSETS) $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD) + $(SDK_ANDROID) update project -p $(PATH_BUILD) -t android-$(ANDROID_TARGET_VERSION) -n ScummVM -$(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png: $(PATH_RESOURCES)/drawable/scummvm.png - @$(MKDIR) -p $(@D) - $(CP) $< $@ +$(APK_MAIN): setupapk | $(PATH_BUILD) + (cd $(PATH_BUILD); ant debug -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))") + $(CP) $(PATH_BUILD)/bin/ScummVM-debug.apk $@ -$(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"; \ - $(RM) -rf $(PATH_BUILD_ASSETS)/tmp; \ - $(MKDIR) $(PATH_BUILD_ASSETS)/tmp; \ - unzip -q $$i -d $(PATH_BUILD_ASSETS)/tmp; \ - cd $(PATH_BUILD_ASSETS)/tmp; \ - 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_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_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 -$(APK_MAIN): $(EXECUTABLE) $(FILE_RESOURCES_MAIN) $(FILE_DEX) - $(INSTALL) -d $(PATH_STAGE_MAIN)/common/lib/armeabi - touch $(PATH_STAGE_MAIN)/common/lib/armeabi/libscummvm.so - $(INSTALL) -d $(PATH_STAGE_MAIN)/common/mylib/armeabi - $(INSTALL) -c -m 644 libscummvm.so $(PATH_STAGE_MAIN)/common/mylib/armeabi/ - $(STRIP) $(PATH_STAGE_MAIN)/common/mylib/armeabi/libscummvm.so - $(APKBUILDER) $@ -z $(FILE_RESOURCES_MAIN) -f $(FILE_DEX) -rf $(PATH_STAGE_MAIN)/common || { $(RM) $@; exit 1; } - -scummvm-engine-%.apk: plugins/lib%.so $(PATH_BUILD)/%/$(FILE_RESOURCES) $(FILE_DEX_PLUGIN) - $(INSTALL) -d $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/ - $(INSTALL) -c -m 644 plugins/lib$*.so $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/ - $(STRIP) $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/lib$*.so - $(APKBUILDER) $@ -z $(PATH_BUILD)/$*/$(FILE_RESOURCES) -f $(FILE_DEX_PLUGIN) -rf $(PATH_STAGE_PREFIX).$*/apk || { $(RM) $@; exit 1; } - -all: $(APK_MAIN) $(APK_PLUGINS) +$(APK_MAIN_RELEASE): setupapk | $(PATH_BUILD) + (cd $(PATH_BUILD); ant release -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))") + $(CP) $(PATH_BUILD)/bin/ScummVM-release-unsigned.apk $@ + +all: $(APK_MAIN) clean: androidclean androidclean: - @$(RM) -rf $(PATH_BUILD) $(PATH_STAGE_PREFIX).* *.apk release - -# remove debugging signature -release/%.apk: %.apk - @$(MKDIR) -p $(@D) - @$(RM) $@ - $(CP) $< $@.tmp - zip -d $@.tmp META-INF/\* - jarsigner $(JARSIGNER_FLAGS) $@.tmp release - zipalign 4 $@.tmp $@ - $(RM) $@.tmp + @$(RM) -rf $(PATH_BUILD) *.apk release -androidrelease: $(addprefix release/, $(APK_MAIN) $(APK_PLUGINS)) +androidrelease: $(APK_MAIN_RELEASE) androidtestmain: $(APK_MAIN) $(ADB) install -r $(APK_MAIN) @@ -198,4 +105,4 @@ androiddistdebug: all sed 's/$$/\r/' < $$i > debug/`basename $$i`.txt; \ done -.PHONY: androidrelease androidtest +.PHONY: androidrelease androidtest $(PATH_BUILD_SRC) diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp index 764c84ce1c..22e6a749c2 100644 --- a/backends/platform/android/jni.cpp +++ b/backends/platform/android/jni.cpp @@ -79,7 +79,6 @@ jmethodID JNI::_MID_displayMessageOnOSD = 0; jmethodID JNI::_MID_setWindowCaption = 0; jmethodID JNI::_MID_showVirtualKeyboard = 0; jmethodID JNI::_MID_getSysArchives = 0; -jmethodID JNI::_MID_getPluginDirectories = 0; jmethodID JNI::_MID_initSurface = 0; jmethodID JNI::_MID_deinitSurface = 0; @@ -293,46 +292,6 @@ void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { } } -void JNI::getPluginDirectories(Common::FSList &dirs) { - JNIEnv *env = JNI::getEnv(); - - jobjectArray array = - (jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories); - - if (env->ExceptionCheck()) { - LOGE("Error finding plugin directories"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - - return; - } - - jsize size = env->GetArrayLength(array); - for (jsize i = 0; i < size; ++i) { - jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); - - if (path_obj == 0) - continue; - - const char *path = env->GetStringUTFChars(path_obj, 0); - - if (path == 0) { - LOGE("Error getting string characters from plugin directory"); - - env->ExceptionClear(); - env->DeleteLocalRef(path_obj); - - continue; - } - - dirs.push_back(Common::FSNode(path)); - - env->ReleaseStringUTFChars(path_obj, path); - env->DeleteLocalRef(path_obj); - } -} - bool JNI::initSurface() { JNIEnv *env = JNI::getEnv(); @@ -454,7 +413,6 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager, FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V"); FIND_METHOD(, showVirtualKeyboard, "(Z)V"); FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;"); - FIND_METHOD(, getPluginDirectories, "()[Ljava/lang/String;"); FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;"); FIND_METHOD(, deinitSurface, "()V"); @@ -543,10 +501,6 @@ jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) { env->DeleteLocalRef(arg); } -#ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new AndroidPluginProvider()); -#endif - LOGI("Entering scummvm_main with %d args", argc); res = scummvm_main(argc, argv); diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h index 326869b1ee..70feaaf72a 100644 --- a/backends/platform/android/jni.h +++ b/backends/platform/android/jni.h @@ -55,7 +55,6 @@ public: static void setReadyForEvents(bool ready); - static void getPluginDirectories(Common::FSList &dirs); static void setWindowCaption(const char *caption); static void getDPI(float *values); static void displayMessageOnOSD(const char *msg); @@ -93,7 +92,6 @@ private: static jmethodID _MID_setWindowCaption; static jmethodID _MID_showVirtualKeyboard; static jmethodID _MID_getSysArchives; - static jmethodID _MID_getPluginDirectories; static jmethodID _MID_initSurface; static jmethodID _MID_deinitSurface; diff --git a/backends/platform/android/org/scummvm/scummvm/PluginProvider.java b/backends/platform/android/org/scummvm/scummvm/PluginProvider.java deleted file mode 100644 index e27e8d41a8..0000000000 --- a/backends/platform/android/org/scummvm/scummvm/PluginProvider.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.scummvm.scummvm; - -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.util.Log; - -import java.util.ArrayList; - -public class PluginProvider extends BroadcastReceiver { - private final static String LOG_TAG = "ScummVM"; - - public final static String META_UNPACK_LIB = - "org.scummvm.scummvm.meta.UNPACK_LIB"; - - public void onReceive(Context context, Intent intent) { - if (!intent.getAction().equals(ScummVMApplication.ACTION_PLUGIN_QUERY)) - return; - - Bundle extras = getResultExtras(true); - - final ActivityInfo info; - final PackageInfo pinfo; - try { - info = context.getPackageManager() - .getReceiverInfo(new ComponentName(context, this.getClass()), - PackageManager.GET_META_DATA); - pinfo = context.getPackageManager() - .getPackageInfo(context.getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - Log.e(LOG_TAG, "Error finding my own info?", e); - return; - } - - String host_version = extras.getString(ScummVMApplication.EXTRA_VERSION); - if (!pinfo.versionName.equals(host_version)) { - Log.e(LOG_TAG, "Plugin version " + pinfo.versionName + " is not equal to ScummVM version " + host_version); - return; - } - - String mylib = info.metaData.getString(META_UNPACK_LIB); - if (mylib != null) { - ArrayList<String> all_libs = - extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS); - all_libs.add(new Uri.Builder() - .scheme("plugin") - .authority(context.getPackageName()) - .path(mylib) - .toString()); - - extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS, - all_libs); - } - - setResultExtras(extras); - } -} diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java index 5047502e61..3b370a583d 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java @@ -54,7 +54,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable { abstract protected void getDPI(float[] values); abstract protected void displayMessageOnOSD(String msg); abstract protected void setWindowCaption(String caption); - abstract protected String[] getPluginDirectories(); abstract protected void showVirtualKeyboard(boolean enable); abstract protected String[] getSysArchives(); @@ -444,10 +443,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable { } } - File cache_dir = ScummVMApplication.getLastCacheDir(); - String libname = System.mapLibraryName("scummvm"); - File libpath = new File(cache_dir, libname); - - System.load(libpath.getPath()); + System.loadLibrary("scummvm"); } } diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java index f4eb7ddd0b..5b2dcae175 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java @@ -84,13 +84,6 @@ public class ScummVMActivity extends Activity { } @Override - protected String[] getPluginDirectories() { - String[] dirs = new String[1]; - dirs[0] = ScummVMApplication.getLastCacheDir().getPath(); - return dirs; - } - - @Override protected void showVirtualKeyboard(final boolean enable) { runOnUiThread(new Runnable() { public void run() { diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMApplication.java b/backends/platform/android/org/scummvm/scummvm/ScummVMApplication.java deleted file mode 100644 index 0adc166222..0000000000 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.scummvm.scummvm; - -import android.app.Application; - -import java.io.File; - -public class ScummVMApplication extends Application { - public final static String ACTION_PLUGIN_QUERY = "org.scummvm.scummvm.action.PLUGIN_QUERY"; - public final static String EXTRA_UNPACK_LIBS = "org.scummvm.scummvm.extra.UNPACK_LIBS"; - public final static String EXTRA_VERSION = "org.scummvm.scummvm.extra.VERSION"; - - private static File _cache_dir; - - @Override - public void onCreate() { - super.onCreate(); - - // This is still on /data :( - _cache_dir = getCacheDir(); - // This is mounted noexec :( - //cache_dir = new File(Environment.getExternalStorageDirectory(), - // "/.ScummVM.tmp"); - // This is owned by download manager and requires special - // permissions to access :( - //cache_dir = Environment.getDownloadCacheDirectory(); - } - - public static File getLastCacheDir() { - return _cache_dir; - } -} diff --git a/backends/platform/android/org/scummvm/scummvm/Unpacker.java b/backends/platform/android/org/scummvm/scummvm/Unpacker.java deleted file mode 100644 index da76ceb5e5..0000000000 --- a/backends/platform/android/org/scummvm/scummvm/Unpacker.java +++ /dev/null @@ -1,388 +0,0 @@ -package org.scummvm.scummvm; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.util.Log; -import android.widget.ProgressBar; - -import java.io.IOException; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.zip.ZipFile; -import java.util.zip.ZipEntry; - -public class Unpacker extends Activity { - protected final static String LOG_TAG = "ScummVM"; - // TODO don't hardcode this - private final static boolean PLUGINS_ENABLED = false; - private final static String META_NEXT_ACTIVITY = - "org.scummvm.unpacker.nextActivity"; - private ProgressBar mProgress; - private File mUnpackDest; // location to unpack into - private AsyncTask<String, Integer, Void> mUnpacker; - private final static int REQUEST_MARKET = 1; - - // Android 3.1+ only - public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32; - - private static class UnpackJob { - public ZipFile zipfile; - public Set<String> paths; - - public UnpackJob(ZipFile zipfile, Set<String> paths) { - this.zipfile = zipfile; - this.paths = paths; - } - - public long UnpackSize() { - long size = 0; - for (String path: paths) { - ZipEntry entry = zipfile.getEntry(path); - if (entry != null) size += entry.getSize(); - } - return size; - } - } - - private class UnpackTask extends AsyncTask<String, Integer, Void> { - @Override - protected void onProgressUpdate(Integer... progress) { - mProgress.setIndeterminate(false); - mProgress.setMax(progress[1]); - mProgress.setProgress(progress[0]); - mProgress.postInvalidate(); - } - - @Override - protected void onPostExecute(Void result) { - Bundle md = getMetaData(); - String nextActivity = md.getString(META_NEXT_ACTIVITY); - if (nextActivity != null) { - final ComponentName cn = - ComponentName.unflattenFromString(nextActivity); - if (cn != null) { - final Intent origIntent = getIntent(); - Intent intent = new Intent(); - intent.setComponent(cn); - if (origIntent.getExtras() != null) - intent.putExtras(origIntent.getExtras()); - intent.putExtra(Intent.EXTRA_INTENT, origIntent); - intent.setDataAndType(origIntent.getData(), - origIntent.getType()); - //intent.fillIn(getIntent(), 0); - intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); - Log.i(LOG_TAG, - "Starting next activity with intent " + intent); - startActivity(intent); - } else { - Log.w(LOG_TAG, - "Unable to extract a component name from " + nextActivity); - } - } - - finish(); - } - - @Override - protected Void doInBackground(String... all_libs) { - // This will contain all unpack jobs - Map<String, UnpackJob> unpack_jobs = - new HashMap<String, UnpackJob>(all_libs.length); - - // This will contain all unpack filenames (so we can - // detect stale files in the unpack directory) - Set<String> all_files = new HashSet<String>(all_libs.length); - - for (String lib: all_libs) { - final Uri uri = Uri.parse(lib); - final String pkg = uri.getAuthority(); - final String path = uri.getPath().substring(1); // skip first / - - all_files.add(new File(path).getName()); - - UnpackJob job = unpack_jobs.get(pkg); - if (job == null) { - try { - // getPackageResourcePath is hidden in Context, - // but exposed in ContextWrapper... - ContextWrapper context = - new ContextWrapper(createPackageContext(pkg, 0)); - ZipFile zipfile = - new ZipFile(context.getPackageResourcePath()); - job = new UnpackJob(zipfile, new HashSet<String>(1)); - } catch (PackageManager.NameNotFoundException e) { - Log.e(LOG_TAG, "Package " + pkg + - " not found", e); - continue; - } catch (IOException e) { - // FIXME: show some sort of GUI error dialog - Log.e(LOG_TAG, - "Error opening ZIP for package " + pkg, e); - continue; - } - unpack_jobs.put(pkg, job); - } - job.paths.add(path); - } - - // Delete stale filenames from mUnpackDest - for (File file: mUnpackDest.listFiles()) { - if (!all_files.contains(file.getName())) { - Log.i(LOG_TAG, - "Deleting stale cached file " + file); - file.delete(); - } - } - - int total_size = 0; - for (UnpackJob job: unpack_jobs.values()) - total_size += job.UnpackSize(); - - publishProgress(0, total_size); - - mUnpackDest.mkdirs(); - - int progress = 0; - - for (UnpackJob job: unpack_jobs.values()) { - try { - ZipFile zipfile = job.zipfile; - for (String path: job.paths) { - ZipEntry zipentry = zipfile.getEntry(path); - if (zipentry == null) - throw new FileNotFoundException( - "Couldn't find " + path + " in zip"); - File dest = new File(mUnpackDest, new File(path).getName()); - if (dest.exists() && - dest.lastModified() == zipentry.getTime() && - dest.length() == zipentry.getSize()) { - // Already unpacked - progress += zipentry.getSize(); - } else { - if (dest.exists()) - Log.d(LOG_TAG, - "Replacing " + dest.getPath() + - " old.mtime=" + dest.lastModified() + - " new.mtime=" + zipentry.getTime() + - " old.size=" + dest.length() + - " new.size=" + zipentry.getSize()); - else - Log.i(LOG_TAG, - "Extracting " + zipentry.getName() + - " from " + zipfile.getName() + - " to " + dest.getPath()); - - long next_update = progress; - - InputStream in = zipfile.getInputStream(zipentry); - OutputStream out = new FileOutputStream(dest); - int len; - byte[] buffer = new byte[4096]; - while ((len = in.read(buffer)) != -1) { - out.write(buffer, 0, len); - progress += len; - if (progress >= next_update) { - publishProgress(progress, total_size); - // Arbitrary limit of 2% update steps - next_update += total_size / 50; - } - } - - in.close(); - out.close(); - dest.setLastModified(zipentry.getTime()); - } - publishProgress(progress, total_size); - } - - zipfile.close(); - } catch (IOException e) { - // FIXME: show some sort of GUI error dialog - Log.e(LOG_TAG, "Error unpacking plugin", e); - } - } - - if (progress != total_size) - Log.d(LOG_TAG, "Ended with progress " + progress + - " != total size " + total_size); - - setResult(RESULT_OK); - - return null; - } - } - - private class PluginBroadcastReciever extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (!intent.getAction() - .equals(ScummVMApplication.ACTION_PLUGIN_QUERY)) { - Log.e(LOG_TAG, - "Received unexpected action " + intent.getAction()); - return; - } - - Bundle extras = getResultExtras(false); - if (extras == null) { - // Nothing for us to do. - Unpacker.this.setResult(RESULT_OK); - finish(); - } - - ArrayList<String> unpack_libs = - extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS); - - if (unpack_libs != null && !unpack_libs.isEmpty()) { - final String[] libs = - unpack_libs.toArray(new String[unpack_libs.size()]); - mUnpacker = new UnpackTask().execute(libs); - } - } - } - - private void initPlugins() { - Bundle extras = new Bundle(1); - - ArrayList<String> unpack_libs = new ArrayList<String>(1); - // This is the common ScummVM code (not really a "plugin" as such) - unpack_libs.add(new Uri.Builder() - .scheme("plugin") - .authority(getPackageName()) - .path("mylib/armeabi/libscummvm.so") - .toString()); - extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS, - unpack_libs); - - final PackageInfo info; - try { - info = getPackageManager().getPackageInfo(getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - Log.e(LOG_TAG, "Error finding my own info?", e); - return; - } - extras.putString(ScummVMApplication.EXTRA_VERSION, info.versionName); - - Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY); - // Android 3.1 defaults to FLAG_EXCLUDE_STOPPED_PACKAGES, and since - // none of our plugins will ever be running, that is not helpful - intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES); - sendOrderedBroadcast(intent, Manifest.permission.SCUMMVM_PLUGIN, - new PluginBroadcastReciever(), - null, RESULT_OK, null, extras); - } - - @Override - public void onCreate(Bundle b) { - super.onCreate(b); - - mUnpackDest = ScummVMApplication.getLastCacheDir(); - - setContentView(R.layout.splash); - mProgress = (ProgressBar)findViewById(R.id.progress); - - setResult(RESULT_CANCELED); - - tryUnpack(); - } - - private void tryUnpack() { - Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY); - List<ResolveInfo> plugins = getPackageManager() - .queryBroadcastReceivers(intent, 0); - if (PLUGINS_ENABLED && plugins.isEmpty()) { - // No plugins installed - AlertDialog.Builder alert = new AlertDialog.Builder(this) - .setTitle(R.string.no_plugins_title) - .setMessage(R.string.no_plugins_found) - .setIcon(android.R.drawable.ic_dialog_alert) - .setOnCancelListener(new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - finish(); - } - }) - .setNegativeButton(R.string.quit, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - - final Uri uri = Uri.parse("market://search?q=ScummVM plugin"); - final Intent market_intent = new Intent(Intent.ACTION_VIEW, uri); - if (getPackageManager().resolveActivity(market_intent, 0) != null) { - alert.setPositiveButton(R.string.to_market, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - try { - startActivityForResult(market_intent, - REQUEST_MARKET); - } catch (ActivityNotFoundException e) { - Log.e(LOG_TAG, - "Error starting market", e); - } - } - }); - } - - alert.show(); - - } else { - // Already have at least one plugin installed - initPlugins(); - } - } - - @Override - public void onStop() { - if (mUnpacker != null) - mUnpacker.cancel(true); - super.onStop(); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent data) { - switch (requestCode) { - case REQUEST_MARKET: - if (resultCode != RESULT_OK) - Log.w(LOG_TAG, "Market returned " + resultCode); - tryUnpack(); - break; - } - } - - private Bundle getMetaData() { - try { - ActivityInfo ai = getPackageManager() - .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); - return ai.metaData; - } catch (PackageManager.NameNotFoundException e) { - Log.w(LOG_TAG, "Unable to find my own meta-data", e); - return new Bundle(); - } - } -} diff --git a/backends/platform/symbian/AdaptAllMMPs.pl b/backends/platform/symbian/AdaptAllMMPs.pl index 6eed7f0690..6b9f918a51 100644 --- a/backends/platform/symbian/AdaptAllMMPs.pl +++ b/backends/platform/symbian/AdaptAllMMPs.pl @@ -48,12 +48,14 @@ chdir("../../../"); "mmp/scummvm_voyeur.mmp", "mmp/scummvm_wintermute.mmp", # New engines + "mmp/scummvm_access.mmp", "mmp/scummvm_avalanche.mmp", "mmp/scummvm_bbvs.mmp", "mmp/scummvm_cge2.mmp", "mmp/scummvm_fullpipe.mmp", "mmp/scummvm_lastexpress.mmp", "mmp/scummvm_mads.mmp", + "mmp/scummvm_prince.mmp", "mmp/scummvm_sword25.mmp", "mmp/scummvm_testbed.mmp", "mmp/scummvm_zvision.mmp", @@ -103,6 +105,7 @@ my @sections_scumm = ("", "ENABLE_SCUMM_7_8", "ENABLE_HE"); # special sections f # files excluded from build, case insensitive, will be matched in filename string only my @excludes_snd = ( "mt32.*", + "Analog.cpp", "fluidsynth.cpp", "i386.cpp", "part.*", @@ -191,6 +194,7 @@ ParseModule("_tucker", "tucker", \@section_empty); ParseModule("_voyeur" ,"voyeur", \@section_empty); ParseModule("_wintermute","wintermute", \@section_empty); ##### new engines +ParseModule("_access" ,"access", \@section_empty); ParseModule("_avalanche" ,"avalanche", \@section_empty); ParseModule("_bbvs" ,"bbvs", \@section_empty); ParseModule("_cge2" ,"cge2", \@section_empty); @@ -198,6 +202,7 @@ ParseModule("_fullpipe" ,"fullpipe", \@section_empty); ParseModule("_lastexpress","lastexpress", \@section_empty); ParseModule("_m4", "m4", \@section_empty); ParseModule("_mads" ,"mads", \@section_empty); +ParseModule("_prince" ,"prince", \@section_empty); ParseModule("_sword25" ,"sword25", \@section_empty); ParseModule("_testbed" ,"testbed", \@section_empty); ParseModule("_zvision" ,"zvision", \@section_empty); diff --git a/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl b/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl index f9b376d674..8c19631524 100644 --- a/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl +++ b/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl @@ -3,32 +3,36 @@ @WorkingEngines = qw( agos agi cine cge composer cruise draci dreamweb - drascula hugo gob groovie kyra lastexpress - lure made mohawk parallaction pegasus queen - saga sci scumm sky sword1 sword2 teenagent tinsel - toltecs tony toon touche tsage tucker wintermute - bbvs fullpipe hopkins mortevielle mads cge2 - neverhood testbed avalanche zvision voyeur + drascula hugo gob groovie hopkins kyra lastexpress + lure made mohawk mortevielle neverhood parallaction + pegasus queen saga sci scumm sky sword1 sword2 + teenagent tinsel toltecs tony toon touche tsage + tucker voyeur wintermute + access avalanche bbvs cge2 fullpipe mads prince + testbed zvision ); -#### New engines -#### sword25 + +#### sword25 yet not added + +#### In progress engines are : +#### access avalanche bbvs cge2 fullpipe mads prince +#### testbed zvision @WorkingEngines_1st = qw( - cge2 cine composer cruise drascula groovie - lastexpress made parallaction queen - saga scumm touche tucker wintermute - avalanche zvision voyeur + cine composer cruise drascula groovie + lastexpress made parallaction queen saga + scumm touche tucker wintermute voyeur + access avalanche cge2 zvision ); @WorkingEngines_2nd = qw( - agi agos bbvs cge draci gob hopkins - hugo kyra lure mohawk pegasus sci - sky sword1 sword2 teenagent mads + agi agos cge draci dreamweb gob hopkins + hugo kyra lure mohawk mortevielle neverhood + pegasus sci sky sword1 sword2 teenagent tinsel tsage toltecs tony toon - dreamweb fullpipe mortevielle - neverhood testbed + bbvs fullpipe mads prince testbed ); -#### sword25 +#### sword25 yet not added @TestingEngines = qw( diff --git a/backends/platform/symbian/help/ScummVM.rtf b/backends/platform/symbian/help/ScummVM.rtf index 8e9d37363c..15b2105ecd 100644 --- a/backends/platform/symbian/help/ScummVM.rtf +++ b/backends/platform/symbian/help/ScummVM.rtf @@ -51,24 +51,27 @@ Synonyms;}{\*\cs33 \additive \super \sbasedon10 endnote reference;}{\s34\ql \fi- {\listoverride\listid-125\listoverridecount0\ls12}{\listoverride\listid-120\listoverridecount0\ls13}{\listoverride\listid-129\listoverridecount0\ls14}{\listoverride\listid-119\listoverridecount0\ls15}{\listoverride\listid-125\listoverridecount0\ls16} {\listoverride\listid-120\listoverridecount0\ls17}{\listoverride\listid-129\listoverridecount0\ls18}{\listoverride\listid-119\listoverridecount0\ls19}{\listoverride\listid-125\listoverridecount0\ls20}{\listoverride\listid-120\listoverridecount0\ls21} {\listoverride\listid-129\listoverridecount0\ls22}{\listoverride\listid-119\listoverridecount0\ls23}{\listoverride\listid-125\listoverridecount0\ls24}{\listoverride\listid-120\listoverridecount0\ls25}{\listoverride\listid-129\listoverridecount0\ls26} -{\listoverride\listid-119\listoverridecount0\ls27}{\listoverride\listid-125\listoverridecount0\ls28}{\listoverride\listid-2\listoverridecount1{\lfolevel\listoverrideformat{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 -\levelold\levelspace0\levelindent283{\leveltext\'01\u-3991 ?;}{\levelnumbers;}\f30\chbrdr\brdrnone\brdrcf1 \chshdng0\chcfpat1\chcbpat1\fbias0 \fi-283\li283 }}\ls29}{\listoverride\listid-2\listoverridecount1{\lfolevel\listoverrideformat{\listlevel -\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelold\levelspace0\levelindent283{\leveltext\'01\u-3991 ?;}{\levelnumbers;}\f30\chbrdr\brdrnone\brdrcf1 \chshdng0\chcfpat1\chcbpat1\fbias0 \fi-283\li283 }}\ls30}}{\info{\author Fedor} -{\operator Fedor}{\creatim\yr2013\mo11\dy30\hr23\min4}{\revtim\yr2014\mo9\dy9\hr19\min22}{\version99}{\edmins93}{\nofpages8}{\nofwords1514}{\nofchars8634}{\*\company DEV}{\nofcharsws0}{\vern8249}}\margl1701\margr850\margt1134\margb1134 -\deftab708\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\horzdoc\dghspace120\dgvspace120\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind4\viewscale100\nolnhtadjtbl \fet0{\*\template -E:\\Documents and Settings\\Administrator\\Application Data\\Microsoft\\\'d8\'e0\'e1\'eb\'ee\'ed\'fb\\cshelp2000.dot}\sectd \linex0\sectdefaultcl {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang -{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang -{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard\plain -\s17\ql \li0\ri0\sa120\widctlpar\nooverflow\faroman\rin0\lin0\itap0 \i\f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\lang1033\langfe1033\langnp1033 Author: Fedor Strizhniou.}{\f28 -\par }{\lang1033\langfe1033\langnp1033 Date: November 2013}{\f28 -\par }{\lang1033\langfe1033\langnp1033 Version: 1.7.0 +{\listoverride\listid-119\listoverridecount0\ls27}{\listoverride\listid-125\listoverridecount0\ls28}{\listoverride\listid-120\listoverridecount0\ls29}{\listoverride\listid-129\listoverridecount0\ls30}{\listoverride\listid-119\listoverridecount0\ls31} +{\listoverride\listid-125\listoverridecount0\ls32}{\listoverride\listid-120\listoverridecount0\ls33}{\listoverride\listid-129\listoverridecount0\ls34}{\listoverride\listid-119\listoverridecount0\ls35}{\listoverride\listid-125\listoverridecount0\ls36} +{\listoverride\listid-120\listoverridecount0\ls37}{\listoverride\listid-129\listoverridecount0\ls38}{\listoverride\listid-119\listoverridecount0\ls39}{\listoverride\listid-125\listoverridecount0\ls40}{\listoverride\listid-2\listoverridecount1{\lfolevel +\listoverrideformat{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelold\levelspace0\levelindent283{\leveltext\'01\u-3991 ?;}{\levelnumbers;}\f30\chbrdr\brdrnone\brdrcf1 \chshdng0\chcfpat1\chcbpat1\fbias0 \fi-283\li283 +}}\ls41}{\listoverride\listid-2\listoverridecount1{\lfolevel\listoverrideformat{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelold\levelspace0\levelindent283{\leveltext\'01\u-3991 ?;}{\levelnumbers;}\f30\chbrdr +\brdrnone\brdrcf1 \chshdng0\chcfpat1\chcbpat1\fbias0 \fi-283\li283 }}\ls42}}{\info{\author Fedor}{\operator Fedor}{\creatim\yr2013\mo11\dy30\hr23\min4}{\revtim\yr2014\mo12\dy29\hr22\min52}{\version102}{\edmins95}{\nofpages8}{\nofwords1514}{\nofchars8634} +{\*\company DEV}{\nofcharsws0}{\vern8249}}\margl1701\margr850\margt1134\margb1134 \deftab708\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\horzdoc\dghspace120\dgvspace120\dghorigin1701\dgvorigin1984\dghshow0 +\dgvshow3\jcompress\viewkind4\viewscale100\nolnhtadjtbl \fet0{\*\template E:\\Documents and Settings\\Administrator\\Application Data\\Microsoft\\\'d8\'e0\'e1\'eb\'ee\'ed\'fb\\cshelp2000.dot}\sectd \linex0\sectdefaultcl {\*\pnseclvl1 +\pnucrm\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta )}}{\*\pnseclvl5 +\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard\plain \s17\ql \li0\ri0\sa120\widctlpar\nooverflow\faroman\rin0\lin0\itap0 \i\f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 { +\lang1033\langfe1033\langnp1033 Author: Fedor Strizhniou.}{\f28 +\par }{\lang1033\langfe1033\langnp1033 Date: November 2014}{\f28 +\par }{\lang1033\langfe1033\langnp1033 Version: 1.8.0 \par }\pard\plain \s1\ql \li0\ri0\sb360\sa240\keepn\widctlpar\nooverflow\faroman\outlinelevel0\rin0\lin0\itap0 \b\f1\fs32\lang2057\langfe1033\kerning28\cgrid\langnp2057\langfenp1033 {ScummVM Help \par }\pard\plain \s16\ql \li0\ri0\sb360\sa240\keepn\widctlpar\nooverflow\faroman\rin0\lin0\itap0 \b\f1\fs32\cf9\lang2057\langfe1033\kerning28\cgrid\langnp2057\langfenp1033 {\lang1033\langfe1033\langnp1033 0x100039ce}{\lang1059\langfe1033\langnp1059 \par }\pard\plain \ql \li0\ri0\sa120\widctlpar\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\f28 \par }\pard\plain \s2\ql \li0\ri0\sb120\sa120\keepn\widctlpar\brdrt\brdrs\brdrw30\brsp20 \brdrb\brdrs\brdrw30\brsp20 \tqr\tx9072\nooverflow\faroman\outlinelevel1\rin0\lin0\itap0 \b\f1\fs24\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\b0\f28 About ScummVM Help -\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls29\pnrnot0 -\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls29\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {About ScummVM Help +\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls41\pnrnot0 +\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls41\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {About ScummVM Help \par }\pard\plain \ql \li0\ri0\sa120\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\f28 \par }{ This help file based on ScummVM forum thread with some elaborations(in Anotherguest section) and text correction. If you wish add some text or translate you may download and modify source document from https://sourceforge.net/projects/scummvms60git/ and t @@ -79,8 +82,8 @@ hen send me to fedor_qd@mail.ru \par }{\f29 \par }\pard\plain \s2\ql \li0\ri0\sb120\sa120\keepn\widctlpar\brdrt\brdrs\brdrw30\brsp20 \brdrb\brdrs\brdrw30\brsp20 \tqr\tx9072{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\outlinelevel1\rin0\lin0\itap0 \b\f1\fs24\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\b0\f28 1st guide -\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls29\pnrnot0 -\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls29\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Controls, Virtual keyboard, Shortcuts, ScummVM, Tips, S60, UIQ, UIQ3, S80, s80, S90, s90 +\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls41\pnrnot0 +\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls41\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Controls, Virtual keyboard, Shortcuts, ScummVM, Tips, S60, UIQ, UIQ3, S80, s80, S90, s90 \par }\pard\plain \ql \li0\ri0\sa120\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\f28 \par }{UIQ3 devices: To the top right (holding the phone portrait) you four icons, from the top they are \par @@ -126,17 +129,17 @@ through directorylist (one hand use perhaps!?) or save games etc. Keyboard mode of events that ScummVM receives from SDL. \par What are these Shrinked, Zoomed and Upscaled modes anyway? \par -\par Shrink displays the game on your screen but in a shrinked way, either in Portrait or Landscape mode, so not all the pixels c -an be seen. Zoom mode uses the maximum resolution of your phone displaying a smaller part of the game zoomed at 1:1 pixels. For scrolling in S60 Zoom mode: 0+Cursor keys to scroll around, 0+Ok button to center view. Upscale tries to fill the larger screen -s on S80/S90 devices in a better way for low resolution games. Currently it uses a pixel interpolation upscaling routine. +\par Shrink displays the game on your screen but in a shrinked way, either in Port +rait or Landscape mode, so not all the pixels can be seen. Zoom mode uses the maximum resolution of your phone displaying a smaller part of the game zoomed at 1:1 pixels. For scrolling in S60 Zoom mode: 0+Cursor keys to scroll around, 0+Ok button to cente +r view. Upscale tries to fill the larger screens on S80/S90 devices in a better way for low resolution games. Currently it uses a pixel interpolation upscaling routine. \par \par You can also use a bluetooth mouse with S60v3 devices to control your game. You need the bluetooth hid library from Hinkka http://koti.mbnet.fi/hinkka/Download.html to get it to work properly. \par }{\f29 \par \par }\pard\plain \s2\ql \li0\ri0\sb120\sa120\keepn\widctlpar\brdrt\brdrs\brdrw30\brsp20 \brdrb\brdrs\brdrw30\brsp20 \tqr\tx9072{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\outlinelevel1\rin0\lin0\itap0 \b\f1\fs24\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\b0\f28 2nd guide -\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls29\pnrnot0 -\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls29\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Controls, Virtual keyboard, Shortcuts, ScummVM, Tips, S60, s60 +\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls41\pnrnot0 +\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls41\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Controls, Virtual keyboard, Shortcuts, ScummVM, Tips, S60, s60 \par }\pard\plain \ql \li0\ri0\sa120\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\f28 \par \par }{More user-friendly guide for Nokia phones (based on N96 but should apply to most phones) @@ -161,9 +164,8 @@ s on S80/S90 devices in a better way for low resolution games. Currently it uses \par }{1 - Change Input. \par This is the option you'll probably use the most. There are three settings; A,C and J. \par -\par A - - This is the "Text Input" mode. It allows you to type directly into ScummVM as if you were using a keyboard. Type the same way you would when sending a text message off of your phone. Please note that the pointer is disabled when in this mode. Don't forge -t to exit Configuration Mode before typing! +\par A - This is the "Text Input" mode. It allows you to type directly into ScummVM as if you were using a keyboard. Type the same way you would when sending a text message o +ff of your phone. Please note that the pointer is disabled when in this mode. Don't forget to exit Configuration Mode before typing! \par \par C - This is the "Cursor" mode. This emulates the arrow keys of the keyboard. Some games require using this instead of the mouse (e.g. the destruction derby section towards the end of Full Throttle). \par @@ -178,8 +180,9 @@ t to exit Configuration Mode before typing! \par Only applies to Landscape mode, simply swaps the screen output between having the phone tilted on its left side or on its right side. \par \par 4 - Toggle Zoom On and Off -\par Zooms in on a portion of the screen. Handy for when you are looking through a screen for items or having trouble reading subtitles. Use the navigation buttons for panning around - the play area. Don't forget you'll have to exit out of Configuration Mode before you can move the pointer again. Exiting Configuration Mode does not reset the zoom level. +\par Zooms in on a portion of the screen. Handy for when you are looking through a screen fo +r items or having trouble reading subtitles. Use the navigation buttons for panning around the play area. Don't forget you'll have to exit out of Configuration Mode before you can move the pointer again. Exiting Configuration Mode does not reset the zoom +level. \par }{\f29 \par }{5 & 6 - Unused \par @@ -200,12 +203,12 @@ t to exit Configuration Mode before typing! \par \par }\pard\plain \s2\ql \li0\ri0\sb120\sa120\keepn\widctlpar\brdrt\brdrs\brdrw30\brsp20 \brdrb\brdrs\brdrw30\brsp20 \tqr\tx9072{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\outlinelevel1\rin0\lin0\itap0 \b\f1\fs24\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\b0\f28 3rd guide -\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls29\pnrnot0 -\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls29\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Controls, Virtual keyboard, Shortcuts, ScummVM, Tips, S60, s60 +\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls41\pnrnot0 +\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls41\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Controls, Virtual keyboard, Shortcuts, ScummVM, Tips, S60, s60 \par }\pard\plain \ql \li0\ri0\sa120\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\f28 \par -\par }{ScummVM keys on Nokia e71 (most likely on any other qwerty-device, too), tested on version 0.14.0svn (Feb. 18 2009 - 05:56:07). Number keys are inserted by first pressing fn-key (leftmost key at bottom row on E71) and then pressing correct key (e.g. 5 is fn+g). You don't have to press both keys simultaneously. +\par }{ScummVM keys on Nokia e71 (most likely on any other qwerty-device, too), tested on version 0.14.0svn (Feb. 18 20 +09 05:56:07). Number keys are inserted by first pressing fn-key (leftmost key at bottom row on E71) and then pressing correct key (e.g. 5 is fn+g). You don't have to press both keys simultaneously. \par \par Basic keys: \par @@ -255,9 +258,8 @@ t to exit Configuration Mode before typing! \par p -- punch (hand) \par \par AGI games (King's Quest, Police Quest etc.): -\par The - games work beautifully on the E71, but there's some stupid bugs (in input). I recall finding some debug keys and "last sentence" / "inventory" -keys in earlier version, but I can't find them any more. Also you can't turn on sirens in Police Quest, which -kinda makes it unplayable. +\par The games work beautifully on the E71, but there's some stupid bugs (in input). I recall finding some debug keys and "last sentence" / "inventory" -keys in earlier version, but I can't find them any more. Also you can't turn on sirens in Police Quest, whi +ch kinda makes it unplayable. \par \par There's good side and bad side to each input mode: \par Keyboard (I use this primarily) @@ -278,9 +280,10 @@ kinda makes it unplayable. \par }{\f28 \par }\pard\plain \s2\ql \li0\ri0\sb120\sa120\keepn\widctlpar\brdrt\brdrs\brdrw30\brsp20 \brdrb\brdrs\brdrw30\brsp20 \tqr\tx9072{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\outlinelevel1\rin0\lin0\itap0 \b\f1\fs24\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {ScummVM1 engines list -\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls30\pnrnot0 -\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls30\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Supported engines -\par }\pard\plain \ql \li0\ri0\sa120\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {agi +\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls42\pnrnot0 +\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls42\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Supported engines +\par }\pard\plain \ql \li0\ri0\sa120\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {access +\par agi \par agos \par \tab AGOS2 \par cge2 @@ -312,8 +315,8 @@ kinda makes it unplayable. \par \par }\pard\plain \s2\ql \li0\ri0\sb120\sa120\keepn\widctlpar\brdrt\brdrs\brdrw30\brsp20 \brdrb\brdrs\brdrw30\brsp20 \tqr\tx9072{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\nooverflow\faroman\outlinelevel1\rin0\lin0\itap0 \b\f1\fs24\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {ScummVM2 engines list -\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls30\pnrnot0 -\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls30\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Supported engines +\par {\pntext\pard\plain\s26 \f30\fs20\lang2057\langfe1033\langnp2057\langfenp1033 \loch\af30\dbch\af0\hich\f30 \'69\tab}}\pard\plain \s26\ql \fi-283\li283\ri0\sa120\widctlpar\brdrb\brdrs\brdrw15\brsp20 {\*\pn \pnlvlblt\ilvl0\ls42\pnrnot0 +\pnf30\pnstart1\pnindent283\pnhang{\pntxtb i}}\nooverflow\faroman\ls42\rin0\lin283\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {Supported engines \par }\pard\plain \ql \li0\ri0\sa120\widctlpar\nooverflow\faroman\rin0\lin0\itap0 \f1\fs20\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {avalanche \par cge \par composer @@ -330,6 +333,7 @@ kinda makes it unplayable. \par \tab RIVEN \par mortevielle \par pegasus +\par prince \par sci \par \tab SCI32 \par sky diff --git a/backends/platform/symbian/mmp/scummvm_access.mmp.in b/backends/platform/symbian/mmp/scummvm_access.mmp.in new file mode 100644 index 0000000000..4f8b258ec0 --- /dev/null +++ b/backends/platform/symbian/mmp/scummvm_access.mmp.in @@ -0,0 +1,52 @@ +/* ScummVM - Graphic Adventure Engine + * 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-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 + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// +// EPOC MMP makefile project for ScummVM +// + +// *** Definitions + +TARGET scummvm_access.lib +TARGETTYPE lib +#include "config.mmh" + +//START_AUTO_MACROS_SLAVE// + + // empty base file, will be updated by Perl build scripts + +//STOP_AUTO_MACROS_SLAVE// + +// *** SOURCE files + +SOURCEPATH ..\..\..\..\engines\access + +//START_AUTO_OBJECTS_ACCESS_// + + // empty base file, will be updated by Perl build scripts + +//STOP_AUTO_OBJECTS_ACCESS_// + diff --git a/backends/platform/symbian/mmp/scummvm_prince.mmp.in b/backends/platform/symbian/mmp/scummvm_prince.mmp.in new file mode 100644 index 0000000000..7dfec04f46 --- /dev/null +++ b/backends/platform/symbian/mmp/scummvm_prince.mmp.in @@ -0,0 +1,52 @@ +/* ScummVM - Graphic Adventure Engine + * 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-2013 The ScummVM project + * Copyright (C) 2014 Strizniou Fedor + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// +// EPOC MMP makefile project for ScummVM +// + +// *** Definitions + +TARGET scummvm_prince.lib +TARGETTYPE lib +#include "config.mmh" + +//START_AUTO_MACROS_SLAVE// + + // empty base file, will be updated by Perl build scripts + +//STOP_AUTO_MACROS_SLAVE// + +// *** SOURCE files + +SOURCEPATH ..\..\..\..\engines\prince + +//START_AUTO_OBJECTS_PRINCE_// + + // empty base file, will be updated by Perl build scripts + +//STOP_AUTO_OBJECTS_PRINCE_// diff --git a/backends/platform/symbian/mmp/scummvm_voyeur.mmp.in b/backends/platform/symbian/mmp/scummvm_voyeur.mmp.in index 86dc32b3f8..4221e34195 100644 --- a/backends/platform/symbian/mmp/scummvm_voyeur.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_voyeur.mmp.in @@ -32,10 +32,7 @@ TARGET scummvm_voyeur.lib TARGETTYPE lib -OPTION MSVC /QIfist /Ob1 /Oy /GF // /QIfist disables use of __ftol2 to avoid linker probs with MS libc: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/vcrefQIfistSuppress_ftol.asp -OPTION GCC -Wno-multichar -Wno-reorder // don't optimize for ARM, platform way too sensitive for that :( just turn off some common warnings -OPTION GCCE -Wno-multichar -Wno-reorder -Wno-unused -Wno-format -fsigned-char -ALWAYS_BUILD_AS_ARM +#include "config.mmh" //START_AUTO_MACROS_SLAVE// @@ -52,20 +49,3 @@ SOURCEPATH ..\..\..\..\engines\voyeur // empty base file, will be updated by Perl build scripts //STOP_AUTO_OBJECTS_VOYEUR_// - -// *** Include paths - -USERINCLUDE ..\..\..\..\engines -USERINCLUDE ..\..\..\.. ..\..\..\..\gui ..\..\..\..\audio ..\src - -SYSTEMINCLUDE \epoc32\include\freetype -SYSTEMINCLUDE \epoc32\include\mpeg2dec -SYSTEMINCLUDE \epoc32\include\jpeg -SYSTEMINCLUDE \epoc32\include\png -SYSTEMINCLUDE \epoc32\include\ESDL -SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version -SYSTEMINCLUDE \epoc32\include\libc -SYSTEMINCLUDE \epoc32\include\theora -SYSTEMINCLUDE \epoc32\include\tremor -SYSTEMINCLUDE \epoc32\include -SYSTEMINCLUDE ..\src // for portdefs.h diff --git a/backends/platform/symbian/mmp/scummvm_zvision.mmp.in b/backends/platform/symbian/mmp/scummvm_zvision.mmp.in index e647275dbc..ea0a7b0102 100644 --- a/backends/platform/symbian/mmp/scummvm_zvision.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_zvision.mmp.in @@ -33,6 +33,7 @@ TARGET scummvm_zvision.lib TARGETTYPE lib +USERINCLUDE ..\..\..\..\engines\zvision\graphics #include "config.mmh" //START_AUTO_MACROS_SLAVE// diff --git a/base/main.cpp b/base/main.cpp index b5de7d94d2..0f5ebc7845 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -523,22 +523,42 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { } #endif + // At this point, we usually return to the launcher. However, the + // game may have requested that one or more other games be "chained" + // to the current one, with optional save slots to start the games + // at. At the time of writing, this is used for the Maniac Mansion + // easter egg in Day of the Tentacle. + + Common::String chainedGame; + int saveSlot = -1; + + ChainedGamesMan.pop(chainedGame, saveSlot); + // Discard any command line options. It's unlikely that the user // wanted to apply them to *all* games ever launched. ConfMan.getDomain(Common::ConfigManager::kTransientDomain)->clear(); - // Clear the active config domain - ConfMan.setActiveDomain(""); + if (!chainedGame.empty()) { + if (saveSlot != -1) { + ConfMan.setInt("save_slot", saveSlot, Common::ConfigManager::kTransientDomain); + } + // Start the chained game + ConfMan.setActiveDomain(chainedGame); + } else { + // Clear the active config domain + ConfMan.setActiveDomain(""); + } PluginManager::instance().loadAllPlugins(); // only for cached manager - } else { GUI::displayErrorDialog(_("Could not find any engine capable of running the selected game")); } // reset the graphics to default setupGraphics(system); - launcherDialog(); + if (0 == ConfMan.getActiveDomain()) { + launcherDialog(); + } } PluginManager::instance().unloadAllPlugins(); PluginManager::destroy(); diff --git a/common/endian.h b/common/endian.h index 6d6563f802..0c6b3db621 100644 --- a/common/endian.h +++ b/common/endian.h @@ -49,6 +49,18 @@ # error No endianness defined #endif +#ifdef HAVE_INT64 +#define SWAP_CONSTANT_64(a) \ + ((uint64)((((a) >> 56) & 0x000000FF) | \ + (((a) >> 40) & 0x0000FF00) | \ + (((a) >> 24) & 0x00FF0000) | \ + (((a) >> 8) & 0xFF000000) | \ + (((a) & 0xFF000000) << 8) | \ + (((a) & 0x00FF0000) << 24) | \ + (((a) & 0x0000FF00) << 40) | \ + (((a) & 0x000000FF) << 56) )) +#endif + #define SWAP_CONSTANT_32(a) \ ((uint32)((((a) >> 24) & 0x00FF) | \ (((a) >> 8) & 0xFF00) | \ @@ -59,6 +71,36 @@ ((uint16)((((a) >> 8) & 0x00FF) | \ (((a) << 8) & 0xFF00) )) + + +/** + * Swap the bytes in a 16 bit word in order to convert LE encoded data to BE + * and vice versa. + */ + +// compilerspecific variants come first, fallback last + +// Test for GCC and if the target has the MIPS rel.2 instructions (we know the psp does) +#if defined(__GNUC__) && (defined(__psp__) || defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)) + + FORCEINLINE uint16 SWAP_BYTES_16(const uint16 a) { + if (__builtin_constant_p(a)) { + return SWAP_CONSTANT_16(a); + } else { + uint16 result; + __asm__ ("wsbh %0,%1" : "=r" (result) : "r" (a)); + return result; + } + } +#else + + inline uint16 SWAP_BYTES_16(const uint16 a) { + return (a >> 8) | (a << 8); + } +#endif + + + /** * Swap the bytes in a 32 bit word in order to convert LE encoded data to BE * and vice versa. @@ -108,32 +150,60 @@ } #endif +#ifdef HAVE_INT64 /** - * Swap the bytes in a 16 bit word in order to convert LE encoded data to BE + * Swap the bytes in a 64 bit word in order to convert LE encoded data to BE * and vice versa. */ -// compilerspecific variants come first, fallback last +// machine/compiler-specific variants come first, fallback last // Test for GCC and if the target has the MIPS rel.2 instructions (we know the psp does) +// #if defined(__GNUC__) && (defined(__psp__) || defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)) - FORCEINLINE uint16 SWAP_BYTES_16(const uint16 a) { + FORCEINLINE uint64 SWAP_BYTES_64(const uint64 a) { if (__builtin_constant_p(a)) { - return SWAP_CONSTANT_16(a); + return SWAP_CONSTANT_64(a); } else { - uint16 result; - __asm__ ("wsbh %0,%1" : "=r" (result) : "r" (a)); - return result; + uint32 low = (uint32)a, high = (uint32)(a >> 32); + low = SWAP_BYTES_32(low); + high = SWAP_BYTES_32(high); + + return (((uint64)low) << 32) | high; } } + +// Test for GCC >= 4.3.0 as this version added the bswap builtin +#elif GCC_ATLEAST(4, 3) + + FORCEINLINE uint64 SWAP_BYTES_64(uint64 a) { + return __builtin_bswap64(a); + } + +#elif defined(_MSC_VER) + + FORCEINLINE uint64 SWAP_BYTES_64(uint64 a) { + return _byteswap_uint64(a); + } + +// generic fallback #else - inline uint16 SWAP_BYTES_16(const uint16 a) { - return (a >> 8) | (a << 8); + inline uint64 SWAP_BYTES_64(uint64 a) { + uint32 low = (uint32)a, high = (uint32)(a >> 32); + uint16 lowLow = (uint16)low, lowHigh = (uint16)(low >> 16), + highLow = (uint16)high, highHigh = (uint16)(high >> 16); + + return ((uint64)(((uint32)(uint16)((lowLow >> 8) | (lowLow << 8)) << 16) | + (uint16)((lowHigh >> 8) | (lowHigh << 8))) << 32) | + (((uint32)(uint16)((highLow >> 8) | (highLow << 8)) << 16) | + (uint16)((highHigh >> 8) | (highHigh << 8))); } #endif +#endif // HAVE_INT64 + /** * A wrapper macro used around four character constants, like 'DATA', to @@ -183,6 +253,18 @@ ((Unaligned32 *)ptr)->val = value; } +#ifdef HAVE_INT64 + FORCEINLINE uint64 READ_UINT64(const void *ptr) { + struct Unaligned64 { uint64 val; } __attribute__ ((__packed__, __may_alias__)); + return ((const Unaligned64 *)ptr)->val; + } + + FORCEINLINE void WRITE_UINT64(void *ptr, uint64 value) { + struct Unaligned64 { uint64 val; } __attribute__((__packed__, __may_alias__)); + ((Unaligned64 *)ptr)->val = value; + } +#endif + #elif !defined(SCUMM_NEED_ALIGNMENT) FORCEINLINE uint16 READ_UINT16(const void *ptr) { @@ -201,6 +283,16 @@ *(uint32 *)(ptr) = value; } +#ifdef HAVE_INT64 + FORCEINLINE uint64 READ_UINT64(const void *ptr) { + return *(const uint64 *)(ptr); + } + + FORCEINLINE void WRITE_UINT64(void *ptr, uint64 value) { + *(uint64 *)(ptr) = value; + } +#endif + // use software fallback by loading each byte explicitely #else @@ -227,6 +319,23 @@ b[2] = (uint8)(value >> 16); b[3] = (uint8)(value >> 24); } +#ifdef HAVE_INT64 + inline uint64 READ_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[7] << 56) | (b[6] << 48) | (b[5] << 40) | (b[4] << 32) | (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); + } + inline void WRITE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + b[2] = (uint8)(value >> 16); + b[3] = (uint8)(value >> 24); + b[4] = (uint8)(value >> 32); + b[5] = (uint8)(value >> 40); + b[6] = (uint8)(value >> 48); + b[7] = (uint8)(value >> 56); + } +#endif # elif defined(SCUMM_BIG_ENDIAN) @@ -250,6 +359,23 @@ b[2] = (uint8)(value >> 8); b[3] = (uint8)(value >> 0); } +#ifdef HAVE_INT64 + inline uint64 READ_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 56) | (b[1] << 48) | (b[2] << 40) | (b[3] << 32) | (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]); + } + inline void WRITE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 56); + b[1] = (uint8)(value >> 48); + b[2] = (uint8)(value >> 40); + b[3] = (uint8)(value >> 32); + b[4] = (uint8)(value >> 24); + b[5] = (uint8)(value >> 16); + b[6] = (uint8)(value >> 8); + b[7] = (uint8)(value >> 0); + } +#endif # endif @@ -283,6 +409,17 @@ #define CONSTANT_BE_32(a) SWAP_CONSTANT_32(a) #define CONSTANT_BE_16(a) SWAP_CONSTANT_16(a) +#ifdef HAVE_INT64 + #define READ_LE_UINT64(a) READ_UINT64(a) + #define WRITE_LE_UINT64(a, v) WRITE_UINT64(a, v) + #define FROM_LE_64(a) ((uint64)(a)) + #define FROM_BE_64(a) SWAP_BYTES_64(a) + #define TO_LE_64(a) ((uint64)(a)) + #define TO_BE_64(a) SWAP_BYTES_64(a) + #define CONSTANT_LE_64(a) ((uint64)(a)) + #define CONSTANT_BE_64(a) SWAP_CONSTANT_64(a) +#endif + // if the unaligned load and the byteswap take alot instructions its better to directly read and invert # if defined(SCUMM_NEED_ALIGNMENT) && !defined(__mips__) @@ -306,6 +443,24 @@ b[2] = (uint8)(value >> 8); b[3] = (uint8)(value >> 0); } +#ifdef HAVE_INT64 + inline uint64 READ_BE_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 56) | (b[1] << 48) | (b[2] << 40) | (b[3] << 32) | (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]); + } + inline void WRITE_BE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 56); + b[1] = (uint8)(value >> 48); + b[2] = (uint8)(value >> 40); + b[3] = (uint8)(value >> 32); + b[4] = (uint8)(value >> 24); + b[5] = (uint8)(value >> 16); + b[6] = (uint8)(value >> 8); + b[7] = (uint8)(value >> 0); + } +#endif + # else inline uint16 READ_BE_UINT16(const void *ptr) { @@ -320,6 +475,14 @@ inline void WRITE_BE_UINT32(void *ptr, uint32 value) { WRITE_UINT32(ptr, SWAP_BYTES_32(value)); } +#ifdef HAVE_INT64 + inline uint64 READ_BE_UINT64(const void *ptr) { + return SWAP_BYTES_64(READ_UINT64(ptr)); + } + inline void WRITE_BE_UINT64(void *ptr, uint64 value) { + WRITE_UINT64(ptr, SWAP_BYTES_64(value)); + } +#endif # endif // if defined(SCUMM_NEED_ALIGNMENT) @@ -349,6 +512,17 @@ #define CONSTANT_BE_32(a) ((uint32)(a)) #define CONSTANT_BE_16(a) ((uint16)(a)) +#ifdef HAVE_INT64 + #define READ_BE_UINT64(a) READ_UINT64(a) + #define WRITE_BE_UINT64(a, v) WRITE_UINT64(a, v) + #define FROM_LE_64(a) SWAP_BYTES_64(a) + #define FROM_BE_64(a) ((uint64)(a)) + #define TO_LE_64(a) SWAP_BYTES_64(a) + #define TO_BE_64(a) ((uint64)(a)) + #define CONSTANT_LE_64(a) SWAP_CONSTANT_64(a) + #define CONSTANT_BE_64(a) ((uint64)(a)) +#endif + // if the unaligned load and the byteswap take alot instructions its better to directly read and invert # if defined(SCUMM_NEED_ALIGNMENT) && !defined(__mips__) @@ -372,6 +546,25 @@ b[2] = (uint8)(value >> 16); b[3] = (uint8)(value >> 24); } + +#ifdef HAVE_INT64 + inline uint64 READ_LE_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[7] << 56) | (b[6] << 48) | (b[5] << 40) | (b[4] << 32) | (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); + } + inline void WRITE_LE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + b[2] = (uint8)(value >> 16); + b[3] = (uint8)(value >> 24); + b[4] = (uint8)(value >> 32); + b[5] = (uint8)(value >> 40); + b[6] = (uint8)(value >> 48); + b[7] = (uint8)(value >> 56); + } +#endif + # else inline uint16 READ_LE_UINT16(const void *ptr) { @@ -386,6 +579,14 @@ inline void WRITE_LE_UINT32(void *ptr, uint32 value) { WRITE_UINT32(ptr, SWAP_BYTES_32(value)); } +#ifdef HAVE_INT64 + inline uint64 READ_LE_UINT64(const void *ptr) { + return SWAP_BYTES_64(READ_UINT64(ptr)); + } + inline void WRITE_LE_UINT64(void *ptr, uint64 value) { + WRITE_UINT64(ptr, SWAP_BYTES_64(value)); + } +#endif # endif // if defined(SCUMM_NEED_ALIGNMENT) diff --git a/common/stream.h b/common/stream.h index 2702068cf3..abe5192b70 100644 --- a/common/stream.h +++ b/common/stream.h @@ -125,6 +125,13 @@ public: write(&value, 4); } +#ifdef HAVE_INT64 + void writeUint64LE(uint64 value) { + value = TO_LE_64(value); + write(&value, 8); + } +#endif + void writeUint16BE(uint16 value) { value = TO_BE_16(value); write(&value, 2); @@ -135,6 +142,13 @@ public: write(&value, 4); } +#ifdef HAVE_INT64 + void writeUint64BE(uint64 value) { + value = TO_BE_64(value); + write(&value, 8); + } +#endif + FORCEINLINE void writeSint16LE(int16 value) { writeUint16LE((uint16)value); } @@ -143,6 +157,12 @@ public: writeUint32LE((uint32)value); } +#ifdef HAVE_INT64 + FORCEINLINE void writeSint64LE(int64 value) { + writeUint64LE((uint64)value); + } +#endif + FORCEINLINE void writeSint16BE(int16 value) { writeUint16BE((uint16)value); } @@ -151,6 +171,12 @@ public: writeUint32BE((uint32)value); } +#ifdef HAVE_INT64 + FORCEINLINE void writeSint64BE(int64 value) { + writeUint64BE((uint64)value); + } +#endif + /** * Write the given string to the stream. * This writes str.size() characters, but no terminating zero byte. @@ -241,6 +267,21 @@ public: return FROM_LE_32(val); } +#ifdef HAVE_INT64 + /** + * Read an unsigned 64-bit word stored in little endian (LSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + uint64 readUint64LE() { + uint64 val; + read(&val, 8); + return FROM_LE_64(val); + } +#endif + /** * Read an unsigned 16-bit word stored in big endian (MSB first) order * from the stream and return it. @@ -267,6 +308,21 @@ public: return FROM_BE_32(val); } +#ifdef HAVE_INT64 + /** + * Read an unsigned 64-bit word stored in big endian (MSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + uint64 readUint64BE() { + uint64 val; + read(&val, 8); + return FROM_BE_64(val); + } +#endif + /** * Read a signed 16-bit word stored in little endian (LSB first) order * from the stream and return it. @@ -289,6 +345,19 @@ public: return (int32)readUint32LE(); } +#ifdef HAVE_INT64 + /** + * Read a signed 64-bit word stored in little endian (LSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + FORCEINLINE int64 readSint64LE() { + return (int64)readUint64LE(); + } +#endif + /** * Read a signed 16-bit word stored in big endian (MSB first) order * from the stream and return it. @@ -311,6 +380,19 @@ public: return (int32)readUint32BE(); } +#ifdef HAVE_INT64 + /** + * Read a signed 64-bit word stored in big endian (MSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + FORCEINLINE int64 readSint64BE() { + return (int64)readUint64BE(); + } +#endif + /** * Read the specified amount of data into a malloc'ed buffer * which then is wrapped into a MemoryReadStream. @@ -435,6 +517,14 @@ public: return (_bigEndian) ? TO_BE_32(val) : TO_LE_32(val); } +#ifdef HAVE_INT64 + uint64 readUint64() { + uint64 val; + read(&val, 8); + return (_bigEndian) ? TO_BE_64(val) : TO_LE_64(val); + } +#endif + FORCEINLINE int16 readSint16() { return (int16)readUint16(); } @@ -442,6 +532,12 @@ public: FORCEINLINE int32 readSint32() { return (int32)readUint32(); } + +#ifdef HAVE_INT64 + FORCEINLINE int64 readSint64() { + return (int64)readUint64(); + } +#endif }; /** @@ -4504,6 +4504,7 @@ $_def_64bit_type_unsigned #else $_def_64bit_type_unsigned #endif +#define HAVE_INT64 EOF fi @@ -4532,6 +4533,8 @@ WIN32PATH=$_win32path AMIGAOSPATH=$_amigaospath STATICLIBPATH=$_staticlibpath +ABI := $ABI + BACKEND := $_backend MODULES += $MODULES MODULE_DIRS += $MODULE_DIRS diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml index db8a9adc54..ba1046e85c 100644 --- a/dists/android/AndroidManifest.xml +++ b/dists/android/AndroidManifest.xml @@ -5,7 +5,6 @@ package="org.scummvm.scummvm" android:versionCode="@ANDROID_VERSIONCODE@" android:versionName="1.8.0git" - android:installLocation="preferExternal" android:sharedUserId="org.scummvm.scummvm"> <!-- This version works on Android 1.5 (SDK 3) and newer, but we @@ -13,7 +12,7 @@ <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8"/> - <application android:name=".ScummVMApplication" + <application android:label="@string/app_name" android:description="@string/app_desc" android:icon="@drawable/scummvm"> @@ -24,28 +23,12 @@ android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN"/> - </intent-filter> - </activity> - - <activity android:name=".Unpacker" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> - <meta-data android:name="org.scummvm.unpacker.nextActivity" - android:value="org.scummvm.scummvm/.ScummVMActivity"/> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> </activity> </application> - <permission android:name="org.scummvm.scummvm.permission.SCUMMVM_PLUGIN" - android:label="@string/scummvm_perm_plugin_label" - android:description="@string/scummvm_perm_plugin_desc" - android:protectionLevel="signature"/> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- Always needs some sort of qwerty keyboard. diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in index bf45ffcc0e..d90e282e3d 100644 --- a/dists/android/AndroidManifest.xml.in +++ b/dists/android/AndroidManifest.xml.in @@ -5,7 +5,6 @@ package="org.scummvm.scummvm" android:versionCode="@ANDROID_VERSIONCODE@" android:versionName="@VERSION@" - android:installLocation="preferExternal" android:sharedUserId="org.scummvm.scummvm"> <!-- This version works on Android 1.5 (SDK 3) and newer, but we @@ -13,7 +12,7 @@ <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8"/> - <application android:name=".ScummVMApplication" + <application android:label="@string/app_name" android:description="@string/app_desc" android:icon="@drawable/scummvm"> @@ -24,28 +23,12 @@ android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN"/> - </intent-filter> - </activity> - - <activity android:name=".Unpacker" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> - <meta-data android:name="org.scummvm.unpacker.nextActivity" - android:value="org.scummvm.scummvm/.ScummVMActivity"/> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> </activity> </application> - <permission android:name="org.scummvm.scummvm.permission.SCUMMVM_PLUGIN" - android:label="@string/scummvm_perm_plugin_label" - android:description="@string/scummvm_perm_plugin_desc" - android:protectionLevel="signature"/> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- Always needs some sort of qwerty keyboard. diff --git a/dists/android/custom_rules.xml b/dists/android/custom_rules.xml new file mode 100644 index 0000000000..5ed81b7273 --- /dev/null +++ b/dists/android/custom_rules.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="custom_rules"> + <!-- Override the built-in aapt task, don't compress assets. --> + <target name="-package-resources" depends="-crunch"> + <!-- only package resources if *not* a library project --> + <do-only-if-not-library elseText="Library project: do not package resources..." > + <aapt executable="${aapt}" + command="package" + versioncode="${version.code}" + versionname="${version.name}" + debug="${build.is.packaging.debug}" + manifest="${out.manifest.abs.file}" + assets="${asset.absolute.dir}" + androidjar="${project.target.android.jar}" + apkfolder="${out.absolute.dir}" + nocrunch="${build.packaging.nocrunch}" + resourcefilename="${resource.package.file.name}" + resourcefilter="${aapt.resource.filter}" + libraryResFolderPathRefid="project.library.res.folder.path" + libraryPackagesRefid="project.library.packages" + libraryRFileRefid="project.library.bin.r.file.path" + previousBuildType="${build.last.target}" + buildType="${build.target}" + ignoreAssets="${aapt.ignore.assets}"> + <res path="${out.res.absolute.dir}" /> + <res path="${resource.absolute.dir}" /> + <nocompress /> <!-- forces no compression on any files in assets or res/raw --> + </aapt> + </do-only-if-not-library> + </target> +</project> diff --git a/dists/android/jni/Android.mk b/dists/android/jni/Android.mk new file mode 100644 index 0000000000..0b3ee4d22e --- /dev/null +++ b/dists/android/jni/Android.mk @@ -0,0 +1,9 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +APP_ABI := $(ABI) +LOCAL_MODULE := scummvm +LOCAL_SRC_FILES := ../libscummvm.so + +include $(PREBUILT_SHARED_LIBRARY) diff --git a/dists/android/mkplugin.sh b/dists/android/mkplugin.sh deleted file mode 100755 index 30a7ef66d6..0000000000 --- a/dists/android/mkplugin.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -if [ $# -ne 5 ]; then - echo "usage: $0 configure plugin template versioncode target" - exit 1 -fi - -CONFIGURE=$1 -PLUGIN_NAME=$2 -TEMPLATE=$3 -PLUGIN_VERSION_CODE=$4 -TARGET=$5 - -PLUGIN_DESC=`sed -n "s/add_engine\s$PLUGIN_NAME\s\"\(.\+\)\"\s.*/\1/p" < $CONFIGURE` - -sed "s|@PLUGIN_NAME@|$PLUGIN_NAME|;s|@PLUGIN_VERSION_CODE@|$PLUGIN_VERSION_CODE|;s|@PLUGIN_DESC@|$PLUGIN_DESC|" < $TEMPLATE > $TARGET diff --git a/dists/android/plugin-manifest.xml b/dists/android/plugin-manifest.xml deleted file mode 100644 index 040d1ea57c..0000000000 --- a/dists/android/plugin-manifest.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.scummvm.scummvm.plugin.@PLUGIN_NAME@" - android:versionCode="@PLUGIN_VERSION_CODE@" - android:versionName="1.8.0git" - android:installLocation="preferExternal" - android:sharedUserId="org.scummvm.scummvm"> - - <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" /> - - <application android:label="@string/app_name" - android:description="@string/app_desc" - android:icon="@drawable/scummvm"> - <receiver android:name="org.scummvm.scummvm.PluginProvider" - android:process="org.scummvm.scummvm"> - <intent-filter> - <action android:name="org.scummvm.scummvm.action.PLUGIN_QUERY"/> - <category android:name="android.intent.category.INFO"/> - </intent-filter> - <meta-data android:name="org.scummvm.scummvm.meta.UNPACK_LIB" - android:value="mylib/armeabi/lib@PLUGIN_NAME@.so" /> - </receiver> - </application> - - <uses-permission android:name="org.scummvm.scummvm.permission.SCUMMVM_PLUGIN"/> - <uses-configuration android:reqFiveWayNav="true" - android:reqKeyboardType="qwerty"/> - - <uses-configuration android:reqTouchScreen="finger" - android:reqKeyboardType="qwerty"/> - - <uses-configuration android:reqTouchScreen="stylus" - android:reqKeyboardType="qwerty"/> -</manifest> diff --git a/dists/android/plugin-manifest.xml.in b/dists/android/plugin-manifest.xml.in deleted file mode 100644 index 4b429097ae..0000000000 --- a/dists/android/plugin-manifest.xml.in +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.scummvm.scummvm.plugin.@PLUGIN_NAME@" - android:versionCode="@PLUGIN_VERSION_CODE@" - android:versionName="@VERSION@" - android:installLocation="preferExternal" - android:sharedUserId="org.scummvm.scummvm"> - - <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" /> - - <application android:label="@string/app_name" - android:description="@string/app_desc" - android:icon="@drawable/scummvm"> - <receiver android:name="org.scummvm.scummvm.PluginProvider" - android:process="org.scummvm.scummvm"> - <intent-filter> - <action android:name="org.scummvm.scummvm.action.PLUGIN_QUERY"/> - <category android:name="android.intent.category.INFO"/> - </intent-filter> - <meta-data android:name="org.scummvm.scummvm.meta.UNPACK_LIB" - android:value="mylib/armeabi/lib@PLUGIN_NAME@.so" /> - </receiver> - </application> - - <uses-permission android:name="org.scummvm.scummvm.permission.SCUMMVM_PLUGIN"/> - <uses-configuration android:reqFiveWayNav="true" - android:reqKeyboardType="qwerty"/> - - <uses-configuration android:reqTouchScreen="finger" - android:reqKeyboardType="qwerty"/> - - <uses-configuration android:reqTouchScreen="stylus" - android:reqKeyboardType="qwerty"/> -</manifest> diff --git a/dists/android/plugin-strings.xml b/dists/android/plugin-strings.xml deleted file mode 100644 index ade37e0aca..0000000000 --- a/dists/android/plugin-strings.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<resources> - <string name="app_name">ScummVM plugin: "@PLUGIN_NAME@"</string> - <string name="app_desc">Game engine for: @PLUGIN_DESC@</string> -</resources> diff --git a/dists/android/project.properties b/dists/android/project.properties new file mode 100644 index 0000000000..730e911f2f --- /dev/null +++ b/dists/android/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-14 diff --git a/dists/android/res/drawable/gradient.xml b/dists/android/res/drawable/gradient.xml deleted file mode 100644 index dbfd9b5b34..0000000000 --- a/dists/android/res/drawable/gradient.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <gradient - android:startColor="#e9bb8b" - android:endColor="#d16e09" - android:angle="315" /> -</shape> diff --git a/dists/android/res/layout/main.xml b/dists/android/res/layout/main.xml index 31aa345cc7..8b0d515d62 100644 --- a/dists/android/res/layout/main.xml +++ b/dists/android/res/layout/main.xml @@ -9,8 +9,4 @@ android:keepScreenOn="true" android:focusable="true" android:focusableInTouchMode="true" - android:layout_marginTop="@dimen/verticalMargin" - android:layout_marginLeft="@dimen/horizontalMargin" - android:layout_marginBottom="@dimen/verticalMargin" - android:layout_marginRight="@dimen/horizontalMargin" /> diff --git a/dists/android/res/layout/splash.xml b/dists/android/res/layout/splash.xml deleted file mode 100644 index e9fd5f70e7..0000000000 --- a/dists/android/res/layout/splash.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:gravity="center" - android:background="@drawable/gradient" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:adjustViewBounds="true" - android:scaleType="fitCenter" - android:src="@drawable/scummvm_big" /> - <ProgressBar android:id="@+id/progress" - style="?android:attr/progressBarStyleHorizontal" - android:layout_width="300dip" - android:layout_height="wrap_content" - android:padding="20dip"/> -</LinearLayout> diff --git a/engines/engine.cpp b/engines/engine.cpp index c63437f800..24008dd073 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -45,6 +45,7 @@ #include "common/taskbar.h" #include "common/textconsole.h" #include "common/translation.h" +#include "common/singleton.h" #include "backends/keymapper/keymapper.h" @@ -101,6 +102,36 @@ static void defaultErrorHandler(const char *msg) { } } +// Chained games manager + +ChainedGamesManager::ChainedGamesManager() { + clear(); +} + +void ChainedGamesManager::clear() { + _chainedGames.clear(); +} + +void ChainedGamesManager::push(const Common::String target, const int slot) { + Game game; + game.target = target; + game.slot = slot; + _chainedGames.push(game); +} + +bool ChainedGamesManager::pop(Common::String &target, int &slot) { + if (_chainedGames.empty()) { + return false; + } + Game game = _chainedGames.pop(); + target = game.target; + slot = game.slot; + return true; +} + +namespace Common { +DECLARE_SINGLETON(ChainedGamesManager); +} Engine::Engine(OSystem *syst) : _system(syst), diff --git a/engines/engine.h b/engines/engine.h index e325cc1ba2..d3415d584c 100644 --- a/engines/engine.h +++ b/engines/engine.h @@ -27,6 +27,8 @@ #include "common/str.h" #include "common/language.h" #include "common/platform.h" +#include "common/queue.h" +#include "common/singleton.h" class OSystem; @@ -334,6 +336,32 @@ protected: }; +// Chained games + +/** + * Singleton class which manages chained games. A chained game is one that + * starts automatically, optionally loading a saved game, instead of returning + * to the launcher. + */ +class ChainedGamesManager : public Common::Singleton<ChainedGamesManager> { +private: + struct Game { + Common::String target; + int slot; + }; + + Common::Queue<Game> _chainedGames; + +public: + ChainedGamesManager(); + void clear(); + void push(const Common::String target, const int slot = -1); + bool pop(Common::String &target, int &slot); +}; + +/** Convenience shortcut for accessing the chained games manager. */ +#define ChainedGamesMan ChainedGamesManager::instance() + // FIXME: HACK for MidiEmu & error() extern Engine *g_engine; diff --git a/engines/fullpipe/inventory.cpp b/engines/fullpipe/inventory.cpp index e79f9c54df..f9b507c50b 100644 --- a/engines/fullpipe/inventory.cpp +++ b/engines/fullpipe/inventory.cpp @@ -126,7 +126,7 @@ void Inventory2::removeItem2(Scene *sceneObj, int itemId, int x, int y, int prio int idx = getInventoryItemIndexById(itemId); if (idx >= 0) { - if (_inventoryItems[idx]->itemId >> 16) { + if (_inventoryItems[idx]->count) { removeItem(itemId, 1); Scene *sc = g_fp->accessScene(_sceneId); diff --git a/engines/fullpipe/mgm.cpp b/engines/fullpipe/mgm.cpp index aacfd5452a..1c8ca2a7b1 100644 --- a/engines/fullpipe/mgm.cpp +++ b/engines/fullpipe/mgm.cpp @@ -155,13 +155,14 @@ void MGM::rebuildTables(int objId) { if (!obj) return; - for (uint i = 0; i < obj->_staticsList.size(); i++) + for (uint i = 0; i < obj->_staticsList.size(); i++) { _items[idx]->statics.push_back((Statics *)obj->_staticsList[i]); + _items[idx]->subItems.push_back(new MGMSubItem); + } + for (uint i = 0; i < obj->_movements.size(); i++) _items[idx]->movements1.push_back((Movement *)obj->_movements[i]); - - _items[idx]->subItems.clear(); } int MGM::getItemIndexById(int objId) { diff --git a/engines/fullpipe/scenes/scene16.cpp b/engines/fullpipe/scenes/scene16.cpp index e9d3a37efd..df005950d2 100644 --- a/engines/fullpipe/scenes/scene16.cpp +++ b/engines/fullpipe/scenes/scene16.cpp @@ -182,7 +182,7 @@ void sceneHandler16_fillMug() { mq = new MessageQueue(g_fp->_currentScene->getMessageQueueById(QU_SC16_BOYOUT), 0, 1); mq->replaceKeyCode(-1, g_vars->scene16_walkingBoy->_okeyCode); - if (!mq || mq->chain(g_vars->scene16_walkingBoy)) + if (mq->chain(g_vars->scene16_walkingBoy)) return; } else { if (!g_vars->scene16_walkingGirl) diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp index 880c2eb0df..de3e1ea728 100644 --- a/engines/fullpipe/statics.cpp +++ b/engines/fullpipe/statics.cpp @@ -1048,8 +1048,11 @@ MessageQueue *StaticANIObject::changeStatics1(int msgNum) { if (_flags & 1) _messageQueueId = mq->_id; } else { - if (!queueMessageQueue(mq)) + if (!queueMessageQueue(mq)) { + delete mq; + return 0; + } g_fp->_globalMessageQueueList->addMessageQueue(mq); } @@ -1594,6 +1597,12 @@ Movement::Movement(Movement *src, int *oldIdxs, int newSize, StaticANIObject *an newSize = src->_dynamicPhases.size(); } + if (!newSize) { + warning("Movement::Movement: newSize = 0"); + + return; + } + _framePosOffsets = (Common::Point **)calloc(newSize, sizeof(Common::Point *)); for (int i = 0; i < newSize; i++) diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 03cd1d06e9..c2a0b8b95d 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -562,12 +562,12 @@ static const uint16 gk1SignatureDay6PoliceSleep[] = { 0x34, SIG_UINT16(0x00dc), // ldi 220 0x65, SIG_ADDTOOFFSET(+1), // aTop cycles (1a for PC, 1c for Mac) 0x32, // jmp [end] - 0 + SIG_END }; static const uint16 gk1PatchDay6PoliceSleep[] = { PATCH_ADDTOOFFSET(+5), - 0x34, SIG_UINT16(0x002a), // ldi 42 + 0x34, PATCH_UINT16(0x002a), // ldi 42 0x65, PATCH_GETORIGINALBYTEADJUST(+9, +2), // aTop seconds (1c for PC, 1e for Mac) PATCH_END }; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 2f95bf7751..5a3b30f7ef 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -81,7 +81,8 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { case GID_LSL1: case GID_LSL5: case GID_SQ1: - _width = 190; + _scriptHeight = 190; + break; default: break; } diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 82a8b4452b..6eab5c752f 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -310,7 +310,7 @@ static const GameSettings gameVariantsTable[] = { // Changed o_getResourceSize to cover all resource types {"farm", "", 0, GID_HEGAME, 6, 73, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, {"puttzoo", "", 0, GID_PUTTZOO, 6, 73, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, - {"puttzoo", "HE 72", 0, GID_PUTTZOO, 6, 72, MDT_NONE, GF_USE_KEY | GF_HE_985, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, + {"puttzoo", "HE 72", 0, GID_PUTTZOO, 6, 72, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, {"puttzoo", "HE 98.5", 0, GID_PUTTZOO, 6, 98, MDT_NONE, GF_USE_KEY | GF_HE_985, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, {"puttzoo", "HE 99", 0, GID_PUTTZOO, 6, 99, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, {"puttzoo", "HE 100", 0, GID_PUTTZOO, 6, 100, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)}, diff --git a/engines/scumm/help.cpp b/engines/scumm/help.cpp index cfb23a392a..2281e954ef 100644 --- a/engines/scumm/help.cpp +++ b/engines/scumm/help.cpp @@ -36,6 +36,8 @@ int ScummHelp::numPages(byte gameId) { case GID_MANIAC: case GID_ZAK: return 4; + case GID_INDY4: + return 5; case GID_INDY3: return 6; case GID_LOOM: @@ -43,7 +45,6 @@ int ScummHelp::numPages(byte gameId) { case GID_MONKEY_VGA: case GID_MONKEY: case GID_MONKEY2: - case GID_INDY4: case GID_TENTACLE: case GID_SAMNMAX: case GID_DIG: @@ -287,10 +288,19 @@ void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platfo ADD_BIND("F3", "Melissa"); ADD_BIND("F4", "Leslie"); } + if (gameId == GID_INDY4) { + ADD_BIND("i", _("Toggle Inventory/IQ Points display")); + ADD_BIND("f", _("Toggle Keyboard/Mouse Fighting (*)")); + ADD_LINE; + ADD_TEXT(_("* Keyboard Fighting is always on,")); + ADD_TEXT(_(" so despite the in-game message this")); + ADD_TEXT(_(" actually toggles Mouse Fighting Off/On")); + } break; case 5: switch (gameId) { case GID_INDY3: + case GID_INDY4: title = _("Fighting controls (numpad):"); ADD_BIND("7", _("Step back")); ADD_BIND("4", _("Step back")); @@ -301,7 +311,9 @@ void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platfo ADD_BIND("9", _("Punch high")); ADD_BIND("6", _("Punch middle")); ADD_BIND("3", _("Punch low")); - ADD_LINE; + if (gameId == GID_INDY4) { + ADD_BIND("0", _("Sucker punch")); + } ADD_LINE; ADD_TEXT(_("These are for Indy on left.")); ADD_TEXT(_("When Indy is on the right,")); diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 0c0f6be73b..e5673c1803 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -149,7 +149,7 @@ void ScummEngine::requestSave(int slot, const Common::String &name) { void ScummEngine::requestLoad(int slot) { _saveLoadSlot = slot; - _saveTemporaryState = false; + _saveTemporaryState = (slot == 100); _saveLoadFlag = 2; // 2 for load } diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp index d2f4133f74..6c81f17f2f 100644 --- a/engines/scumm/script_v6.cpp +++ b/engines/scumm/script_v6.cpp @@ -2597,7 +2597,11 @@ void ScummEngine_v6::o6_kernelSetFunctions() { fadeIn(args[1]); break; case 8: - startManiac(); + if (startManiac()) { + // This is so that the surprised exclamation happens + // after we return to the game again, not before. + o6_breakHere(); + } break; case 9: killAllScriptsExceptCurrent(); diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 6040344c2c..7d927b0cda 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -2597,9 +2597,52 @@ void ScummEngine_v90he::runBootscript() { } #endif -void ScummEngine::startManiac() { - debug(0, "stub startManiac()"); - displayMessage(0, "%s", _("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.")); +bool ScummEngine::startManiac() { + Common::String currentPath = ConfMan.get("path"); + Common::String maniacTarget; + + if (!ConfMan.hasKey("easter_egg")) { + // Look for a game with a game path pointing to a 'Maniac' directory + // as a subdirectory to the current game. + Common::ConfigManager::DomainMap::iterator iter = ConfMan.beginGameDomains(); + for (; iter != ConfMan.endGameDomains(); ++iter) { + Common::ConfigManager::Domain &dom = iter->_value; + Common::String path = dom.getVal("path"); + + if (path.hasPrefix(currentPath)) { + path.erase(0, currentPath.size() + 1); + if (path.equalsIgnoreCase("maniac")) { + maniacTarget = dom.getVal("gameid"); + break; + } + } + } + } else { + maniacTarget = ConfMan.get("easter_egg"); + } + + if (!maniacTarget.empty()) { + // Request a temporary save game to be made. + _saveLoadFlag = 1; + _saveLoadSlot = 100; + _saveTemporaryState = true; + + // Set up the chanined games to Maniac Mansion, and then back + // to the current game again with that save slot. + ChainedGamesMan.push(maniacTarget); + ChainedGamesMan.push(ConfMan.getActiveDomainName(), 100); + + // Force a return to the launcher. This will start the first + // chained game. + Common::EventManager *eventMan = g_system->getEventManager(); + Common::Event event; + event.type = Common::EVENT_RTL; + eventMan->pushEvent(event); + return true; + } else { + displayMessage(0, "%s", _("Usually, Maniac Mansion would start now. But for that to work, the game files for Maniac Mansion have to be in the 'Maniac' directory inside the Tentacle game directory, and the game has to be added to ScummVM.")); + return false; + } } #pragma mark - diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 967909e505..30b4d61880 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -654,7 +654,7 @@ protected: int getScriptSlot(); void startScene(int room, Actor *a, int b); - void startManiac(); + bool startManiac(); public: void runScript(int script, bool freezeResistant, bool recursive, int *lvarptr, int cycle = 0); diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp index 9003b5d58a..3e25fce5c7 100644 --- a/engines/sword25/kernel/outputpersistenceblock.cpp +++ b/engines/sword25/kernel/outputpersistenceblock.cpp @@ -41,6 +41,13 @@ OutputPersistenceBlock::OutputPersistenceBlock() { _data.reserve(INITIAL_BUFFER_SIZE); } +void OutputPersistenceBlock::write(const void *data, uint32 size) { + writeMarker(BLOCK_MARKER); + + write(size); + rawWrite(data, size); +} + void OutputPersistenceBlock::write(int32 value) { writeMarker(SINT_MARKER); value = TO_LE_32(value); diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h index c7d8dfc6aa..bdbd20d3e0 100644 --- a/engines/sword25/kernel/outputpersistenceblock.h +++ b/engines/sword25/kernel/outputpersistenceblock.h @@ -41,6 +41,7 @@ class OutputPersistenceBlock : public PersistenceBlock { public: OutputPersistenceBlock(); + void write(const void *data, uint32 size); void write(int32 value); void write(uint32 value); void write(float value); diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 234baec165..0842eb9aa8 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -82,9 +82,10 @@ MODULE_OBJS := \ util/lua/lvm.o \ util/lua/lzio.o \ util/lua/scummvm_file.o \ - util/pluto/pdep.o \ - util/pluto/pluto.o \ - util/pluto/plzio.o + util/double_serialization.o \ + util/lua_persistence_util.o \ + util/lua_persist.o \ + util/lua_unpersist.o # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp index f62a08005b..e93289596b 100644 --- a/engines/sword25/script/luascript.cpp +++ b/engines/sword25/script/luascript.cpp @@ -29,7 +29,7 @@ * */ -#include "common/array.h" +#include "common/memstream.h" #include "common/debug-channels.h" #include "sword25/sword25.h" @@ -43,7 +43,7 @@ #include "sword25/util/lua/lua.h" #include "sword25/util/lua/lualib.h" #include "sword25/util/lua/lauxlib.h" -#include "sword25/util/pluto/pluto.h" +#include "sword25/util/lua_persistence.h" namespace Sword25 { @@ -112,10 +112,6 @@ bool LuaScriptEngine::init() { // Place the error handler function in the Lua registry, and remember the index _pcallErrorhandlerRegistryIndex = luaL_ref(_state, LUA_REGISTRYINDEX); - // Initialize the Pluto-Persistence library - luaopen_pluto(_state); - lua_pop(_state, 1); - // Initialize debugging callback if (DebugMan.isDebugChannelEnabled(kDebugScript)) { int mask = 0; @@ -383,19 +379,8 @@ bool pushPermanentsTable(lua_State *L, PERMANENT_TABLE_TYPE tableType) { return true; } -} - -namespace { -int chunkwriter(lua_State *L, const void *p, size_t sz, void *ud) { - Common::Array<byte> & chunkData = *reinterpret_cast<Common::Array<byte> * >(ud); - const byte *buffer = reinterpret_cast<const byte *>(p); - while (sz--) - chunkData.push_back(*buffer++); - - return 1; -} -} +} // End of anonymous namespace bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters @@ -409,12 +394,12 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { pushPermanentsTable(_state, PTT_PERSIST); lua_getglobal(_state, "_G"); - // Lua persists and stores the data in a Common::Array - Common::Array<byte> chunkData; - pluto_persist(_state, chunkwriter, &chunkData); + // Lua persists and stores the data in a WriteStream + Common::MemoryWriteStreamDynamic writeStream; + Lua::persistLua(_state, &writeStream); // Persistenzdaten in den Writer schreiben. - writer.writeByteArray(chunkData); + writer.write(writeStream.getData(), writeStream.size()); // Die beiden Tabellen vom Stack nehmen. lua_pop(_state, 2); @@ -424,24 +409,6 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { namespace { -struct ChunkreaderData { - void *BufferPtr; - size_t Size; - bool BufferReturned; -}; - -const char *chunkreader(lua_State *L, void *ud, size_t *sz) { - ChunkreaderData &cd = *reinterpret_cast<ChunkreaderData *>(ud); - - if (!cd.BufferReturned) { - cd.BufferReturned = true; - *sz = cd.Size; - return reinterpret_cast<const char *>(cd.BufferPtr); - } else { - return 0; - } -} - void clearGlobalTable(lua_State *L, const char **exceptions) { // Iterate over all elements of the global table lua_pushvalue(L, LUA_GLOBALSINDEX); @@ -479,7 +446,8 @@ void clearGlobalTable(lua_State *L, const char **exceptions) { // Perform garbage collection, so that all removed elements are deleted lua_gc(L, LUA_GCCOLLECT, 0); } -} + +} // End of anonymous namespace bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters @@ -512,14 +480,9 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // Persisted Lua data Common::Array<byte> chunkData; reader.readByteArray(chunkData); + Common::MemoryReadStream readStream(&chunkData[0], chunkData.size(), DisposeAfterUse::NO); - // Chunk-Reader initialisation. It is used with pluto_unpersist to restore read data - ChunkreaderData cd; - cd.BufferPtr = &chunkData[0]; - cd.Size = chunkData.size(); - cd.BufferReturned = false; - - pluto_unpersist(_state, chunkreader, &cd); + Lua::unpersistLua(_state, &readStream); // Permanents-Table is removed from stack lua_remove(_state, -2); @@ -527,7 +490,7 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // The read elements in the global table about lua_pushnil(_state); while (lua_next(_state, -2) != 0) { - // The referenec to the global table (_G) must not be overwritten, or ticks from Lua total + // The reference to the global table (_G) must not be overwritten, or ticks from Lua total bool isGlobalReference = lua_isstring(_state, -2) && strcmp(lua_tostring(_state, -2), "_G") == 0; if (!isGlobalReference) { lua_pushvalue(_state, -2); diff --git a/engines/sword25/util/double_serialization.cpp b/engines/sword25/util/double_serialization.cpp new file mode 100644 index 0000000000..73da296e40 --- /dev/null +++ b/engines/sword25/util/double_serialization.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. + * + */ + +#include "sword25/util/double_serialization.h" + +#include "common/scummsys.h" + + +namespace Util { + +SerializedDouble encodeDouble(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 31)); + + SerializedDouble returnValue; + returnValue.significandOne = significandOne; // SignificandOne + returnValue.signAndSignificandTwo = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + significandTwo; // SignificandTwo + returnValue.exponent = (int16)exponent; + return returnValue; +} + +double decodeDouble(SerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)value.exponent; + double expandedsignificandOne = (double)value.significandOne; + double expandedsignificandTwo = (double)(value.signAndSignificandTwo & 0x7FFFFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandTwo & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/util/double_serialization.h b/engines/sword25/util/double_serialization.h new file mode 100644 index 0000000000..af58d03c17 --- /dev/null +++ b/engines/sword25/util/double_serialization.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 DOUBLE_SERIALIZATION_H +#define DOUBLE_SERIALIZATION_H + +#include "common/types.h" + + +namespace Util { + +struct SerializedDouble { + uint32 significandOne; + uint32 signAndSignificandTwo; + int16 exponent; +}; + +struct CompactSerializedDouble { + uint32 signAndSignificandOne; + uint32 exponentAndSignificandTwo; +}; + +/** + * Encodes a double as two uint32 and a one int16 + * + * Supports denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +SerializedDouble encodeDouble(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble(SerializedDouble value); + +} // End of namespace Sword25 + +#endif diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp new file mode 100644 index 0000000000..03f305b2c5 --- /dev/null +++ b/engines/sword25/util/lua_persist.cpp @@ -0,0 +1,802 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serialization.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +struct SerializationInfo { + lua_State *luaState; + Common::WriteStream *writeStream; + uint counter; +}; + +static void persist(SerializationInfo *info); + +static void persistBoolean(SerializationInfo *info); +static void persistNumber(SerializationInfo *info); +static void persistString(SerializationInfo *info); +static void persistTable(SerializationInfo *info); +static void persistFunction(SerializationInfo *info); +static void persistThread(SerializationInfo *info); +static void persistProto(SerializationInfo *info); +static void persistUpValue(SerializationInfo *info); +static void persistUserData(SerializationInfo *info); + + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { + SerializationInfo info; + info.luaState = luaState; + info.writeStream = writeStream; + info.counter = 1u; + + // The process starts with the lua stack as follows: + // >>>>> permTbl rootObj + // That's the table of permanents and the root object to be serialized + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 4); + assert(lua_gettop(luaState) == 2); + // And that the root isn't nil + assert(!lua_isnil(luaState, 2)); + + // Create a table to hold indexes of everything that's serialized + // This allows us to only serialize an object once + // Every other time, just reference the index + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl + + // Now we're going to make the table weakly keyed. This prevents the + // GC from visiting it and trying to mark things it doesn't want to + // mark in tables, e.g. upvalues. All objects in the table are + // a priori reachable, so it doesn't matter that we do this. + + // Create the metatable + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_pushstring(luaState, "__mode"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" + + lua_pushstring(luaState, "k"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" "k" + + lua_settable(luaState, 4); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_setmetatable(luaState, 3); + // >>>>> permTbl rootObj indexTbl + + // Swap the indexTable and the rootObj + lua_insert(luaState, 2); + // >>>>> permTbl indexTbl rootObj + + // Serialize the root recursively + persist(&info); + + // Return the stack back to the original state + lua_remove(luaState, 2); + // >>>>> permTbl rootObj +} + +static void persist(SerializationInfo *info) { + // The stack can potentially have many things on it + // The object we want to serialize is the item on the top of the stack + // >>>>> permTbl indexTbl rootObj ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // If the object has already been written, don't write it again + // Instead write the index of the object from the indexTbl + + // Check the indexTbl + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_rawget(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj ?index? + + // If the index isn't nil, the object has already been written + if (!lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + + // Retrieve the index from the stack + uint *index = (uint *)lua_touserdata(info->luaState, -1); + + // Write out the index + info->writeStream->writeUint32LE(*index); + + // Pop the index off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the index/nil off the stack + lua_pop(info->luaState, 1); + + // If the obj itself is nil, we represent it as an index of 0 + if (lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + // Write out the index + info->writeStream->writeUint32LE(0); + + return; + } + + // Write out a flag that indicates that this is a real object + info->writeStream->writeByte(1); + + // Add the object to the indexTbl + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + uint *ref = (uint *)lua_newuserdata(info->luaState, sizeof(uint)); + *ref = ++(info->counter); + // >>>>> permTbl indexTbl rootObj ...... obj obj index + + lua_rawset(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj + + + // Write out the index + info->writeStream->writeUint32LE(info->counter); + + + // Objects that are in the permanents table are serialized in a special way + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl rootObj ...... obj obj ?permKey? + + if (!lua_isnil(info->luaState, -1)) { + // Write out the type + info->writeStream->writeSint32LE(PERMANENT_TYPE); + + // Serialize the key + persist(info); + + // Pop the key off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the nil off the stack + lua_pop(info->luaState, 1); + + // Query the type of the object + int objType = lua_type(info->luaState, -1); + + // Write it out + info->writeStream->writeSint32LE(objType); + + // Serialize the object by its type + + switch (objType) { + case LUA_TBOOLEAN: + persistBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + persistNumber(info); + break; + case LUA_TSTRING: + persistString(info); + break; + case LUA_TTABLE: + persistTable(info); + break; + case LUA_TFUNCTION: + persistFunction(info); + break; + case LUA_TTHREAD: + persistThread(info); + break; + case LUA_TPROTO: + persistProto(info); + break; + case LUA_TUPVAL: + persistUpValue(info); + break; + case LUA_TUSERDATA: + persistUserData(info); + break; + default: + assert(0); + } +} + +static void persistBoolean(SerializationInfo *info) { + int value = lua_toboolean(info->luaState, -1); + + info->writeStream->writeSint32LE(value); +} + +static void persistNumber(SerializationInfo *info) { + lua_Number value = lua_tonumber(info->luaState, -1); + + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); +} + +static void persistString(SerializationInfo *info) { + // Hard cast to a uint32 to force size_t to an explicit size + // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems + uint32 length = static_cast<uint32>(lua_strlen(info->luaState, -1)); + info->writeStream->writeUint32LE(length); + + const char *str = lua_tostring(info->luaState, -1); + info->writeStream->write(str, length); +} + +/* Choose whether to do a regular or special persistence based on an object's + * metatable. "default" is whether the object, if it doesn't have a __persist + * entry, is literally persistable or not. + * Pushes the unpersist closure and returns true if special persistence is + * used. */ +static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 4); + + // Check whether we should persist literally, or via the __persist metafunction + if (!lua_getmetatable(info->luaState, -1)) { + if (defaction) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Not reached + } + } + + // >>>>> permTbl indexTbl ...... obj metaTbl + lua_pushstring(info->luaState, "__persist"); + // >>>>> permTbl indexTbl rootObj ...... obj metaTbl "__persist" + + lua_rawget(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj metaTbl ?__persist? + + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl nil + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + if (defaction) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Return false + } + + } else if (lua_isboolean(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl bool + if (lua_toboolean(info->luaState, -1)) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + // >>>>> permTbl indexTbl ...... obj metaTbl true */ + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return false; + } else { + lua_pushstring(info->luaState, "Metatable forbade persistence"); + lua_error(info->luaState); + + return false; // Not reached + } + } else if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl __persist + lua_pushvalue(info->luaState, -3); + // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj + + // >>>>> permTbl indexTbl ...... obj metaTbl ?func? + + if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist function did not return a function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl func + + // Write out a flag that the function exists + info->writeStream->writeSint32LE(1); + + // Serialize the function + persist(info); + + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return true; +} + +static void persistTable(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... tbl + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 1)) { + return; + } + + // >>>>> permTbl indexTbl ...... tbl + + // First, serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl nil + + // Now, persist all k/v pairs + while (lua_next(info->luaState, -2)) { + // >>>>> permTbl indexTbl ...... tbl k v */ + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl k v k */ + + // Serialize the key + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k v */ + + // Serialize the value + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k */ + } + + // >>>>> permTbl indexTbl ...... tbl + + // Terminate the list with a nil + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void persistFunction(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... func + Closure *cl = clvalue(getObject(info->luaState, -1)); + lua_checkstack(info->luaState, 2); + + if (cl->c.isC) { + /* It's a C function. For now, we aren't going to allow + * persistence of C closures, even if the "C proto" is + * already in the permanents table. */ + lua_pushstring(info->luaState, "Attempt to persist a C function"); + lua_error(info->luaState); + } else { + // It's a Lua closure + + // We don't really _NEED_ the number of upvals, but it'll simplify things a bit + info->writeStream->writeByte(cl->l.p->nups); + + // Serialize the prototype + pushProto(info->luaState, cl->l.p); + // >>>>> permTbl indexTbl ...... func proto */ + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // Serialize upvalue values (not the upvalue objects themselves) + for (byte i = 0; i < cl->l.p->nups; i++) { + // >>>>> permTbl indexTbl ...... func + pushUpValue(info->luaState, cl->l.upvals[i]); + // >>>>> permTbl indexTbl ...... func upval + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func + + // Serialize function environment + lua_getfenv(info->luaState, -1); + // >>>>> permTbl indexTbl ...... func fenv + + if (lua_equal(info->luaState, -1, LUA_GLOBALSINDEX)) { + // Function has the default fenv + + // >>>>> permTbl indexTbl ...... func _G + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + } + + // >>>>> permTbl indexTbl ...... func fenv/nil + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } +} + +static void persistThread(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... thread + lua_State *threadState = lua_tothread(info->luaState, -1); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, threadState->top - threadState->stack + 1); + + if (info->luaState == threadState) { + lua_pushstring(info->luaState, "Can't persist currently running thread"); + lua_error(info->luaState); + return; /* not reached */ + } + + // Persist the stack + + // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 stackSize = static_cast<uint32>(appendStackToStack_reverse(threadState, info->luaState)); + info->writeStream->writeUint32LE(stackSize); + + // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ + for (; stackSize > 0; --stackSize) { + persist(info); + + lua_pop(info->luaState, 1); + } + + // >>>>> permTbl indexTbl ...... thread + + // Now, serialize the CallInfo stack + + // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 numFrames = static_cast<uint32>((threadState->ci - threadState->base_ci) + 1); + info->writeStream->writeUint32LE(numFrames); + + for (uint32 i = 0; i < numFrames; i++) { + CallInfo *ci = threadState->base_ci + i; + + // Same argument as above about truncation + uint32 stackBase = static_cast<uint32>(ci->base - threadState->stack); + uint32 stackFunc = static_cast<uint32>(ci->func - threadState->stack); + uint32 stackTop = static_cast<uint32>(ci->top - threadState->stack); + + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + info->writeStream->writeUint32LE(stackTop); + + info->writeStream->writeSint32LE(ci->nresults); + + uint32 savedpc = (ci != threadState->base_ci) ? static_cast<uint32>(ci->savedpc - ci_func(ci)->l.p->code) : 0u; + info->writeStream->writeUint32LE(savedpc); + } + + + // Serialize the state's other parameters, with the exception of upval stuff + + assert(threadState->nCcalls <= 1); + info->writeStream->writeByte(threadState->status); + + // Same argument as above about truncation + uint32 stackBase = static_cast<uint32>(threadState->base - threadState->stack); + uint32 stackFunc = static_cast<uint32>(threadState->top - threadState->stack); + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + + // Same argument as above about truncation + uint32 stackOffset = static_cast<uint32>(threadState->errfunc); + info->writeStream->writeUint32LE(stackOffset); + + // Finally, record upvalues which need to be reopened + // See the comment above serializeUpVal() for why we do this + + UpVal *upVal; + + // >>>>> permTbl indexTbl ...... thread + for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) { + upVal = gco2uv(gcObject); + + /* Make sure upvalue is really open */ + assert(upVal->v != &upVal->u.value); + + pushUpValue(info->luaState, upVal); + // >>>>> permTbl indexTbl ...... thread upVal + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + // Same argument as above about truncation + uint32 stackpos = static_cast<uint32>(upVal->v - threadState->stack); + info->writeStream->writeUint32LE(stackpos); + } + + // >>>>> permTbl indexTbl ...... thread + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... thread nil + + // Use nil as a terminator + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread +} + +static void persistProto(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... proto + Proto *proto = gco2p(getObject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Serialize constant refs */ + info->writeStream->writeSint32LE(proto->sizek); + + for (int i = 0; i < proto->sizek; ++i) { + pushObject(info->luaState, &proto->k[i]); + // >>>>> permTbl indexTbl ...... proto const + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize inner Proto refs + info->writeStream->writeSint32LE(proto->sizep); + + for (int i = 0; i < proto->sizep; ++i) { + pushProto(info->luaState, proto->p[i]); + // >>>>> permTbl indexTbl ...... proto subProto */ + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize the code + info->writeStream->writeSint32LE(proto->sizecode); + + uint32 len = static_cast<uint32>(sizeof(Instruction) * proto->sizecode); + info->writeStream->write(proto->code, len); + + + // Serialize upvalue names + info->writeStream->writeSint32LE(proto->sizeupvalues); + + for (int i = 0; i < proto->sizeupvalues; ++i) { + pushString(info->luaState, proto->upvalues[i]); + // >>>>> permTbl indexTbl ...... proto str + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + + // Serialize local variable infos + info->writeStream->writeSint32LE(proto->sizelocvars); + + for (int i = 0; i < proto->sizelocvars; ++i) { + pushString(info->luaState, proto->locvars[i].varname); + // >>>>> permTbl indexTbl ...... proto str + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + info->writeStream->writeSint32LE(proto->locvars[i].startpc); + info->writeStream->writeSint32LE(proto->locvars[i].endpc); + } + + + // Serialize source string + pushString(info->luaState, proto->source); + // >>>>> permTbl indexTbl ...... proto sourceStr + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Serialize line numbers + info->writeStream->writeSint32LE(proto->sizelineinfo); + + if (proto->sizelineinfo) { + len = static_cast<uint32>(sizeof(int) * proto->sizelineinfo); + info->writeStream->write(proto->lineinfo, len); + } + + // Serialize linedefined and lastlinedefined + info->writeStream->writeSint32LE(proto->linedefined); + info->writeStream->writeSint32LE(proto->lastlinedefined); + + + // Serialize misc values + info->writeStream->writeByte(proto->nups); + info->writeStream->writeByte(proto->numparams); + info->writeStream->writeByte(proto->is_vararg); + info->writeStream->writeByte(proto->maxstacksize); +} + +/* Upvalues are tricky. Here's why. + * + * A particular upvalue may be either "open", in which case its member v + * points into a thread's stack, or "closed" in which case it points to the + * upvalue itself. An upvalue is closed under any of the following conditions: + * -- The function that initially declared the variable "local" returns + * -- The thread in which the closure was created is garbage collected + * + * To make things wackier, just because a thread is reachable by Lua doesn't + * mean it's in our root set. We need to be able to treat an open upvalue + * from an unreachable thread as a closed upvalue. + * + * The solution: + * (a) For the purposes of serializing, don't indicate whether an upvalue is + * closed or not. + * (b) When unserializing, pretend that all upvalues are closed. + * (c) When serializing, persist all open upvalues referenced by a thread + * that is persisted, and tag each one with the corresponding stack position + * (d) When unserializing, "reopen" each of these upvalues as the thread is + * unserialized + */ +static void persistUpValue(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... upval + assert(ttype(getObject(info->luaState, -1)) == LUA_TUPVAL); + UpVal *upValue = gco2uv(getObject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // We can't permit the upValue to linger around on the stack, as Lua + // will bail if its GC finds it. + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... + + pushObject(info->luaState, upValue->v); + // >>>>> permTbl indexTbl ...... obj + + persist(info); + // >>>>> permTbl indexTbl ...... obj +} + +static void persistUserData(SerializationInfo *info) { + // >>>>> permTbl rootObj ...... udata + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 0)) { + return; + } + + // Use literal persistence + + // Hard cast to a uint32 length + // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems + uint32 length = static_cast<uint32>(uvalue(getObject(info->luaState, -1))->len); + info->writeStream->writeUint32LE(length); + + info->writeStream->write(lua_touserdata(info->luaState, -1), length); + + // Serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl rootObj ...... udata metaTbl/nil + persist(info); + + lua_pop(info->luaState, 1); + /* perms reftbl ... udata */ +} + + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence.h b/engines/sword25/util/lua_persistence.h new file mode 100644 index 0000000000..53e3dee02e --- /dev/null +++ b/engines/sword25/util/lua_persistence.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 code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +#ifndef LUA_PERSISTENCE_H +#define LUA_PERSISTENCE_H + +#include "sword25/util/lua/lua.h" + + +namespace Common { +class WriteStream; +class ReadStream; +} + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream); +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_persistence_util.cpp b/engines/sword25/util/lua_persistence_util.cpp new file mode 100644 index 0000000000..958fb7ac3c --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.cpp @@ -0,0 +1,393 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distri8buted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 heavily based on the pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence_util.h" + +#include "common/scummsys.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { + global_State *globalState = G(luaState); + + block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); + globalState->totalbytes = (globalState->totalbytes - osize) + nsize; + + return block; +} + +void pushObject(lua_State *luaState, TValue *obj) { + setobj2s(luaState, luaState->top, obj); + + api_check(luaState, luaState->top < luaState->ci->top); + luaState->top++; +} + +void pushProto(lua_State *luaState, Proto *proto) { + TValue obj; + setptvalue(luaState, &obj, proto); + + pushObject(luaState, &obj); +} + +void pushUpValue(lua_State *luaState, UpVal *upval) { + TValue obj; + + obj.value.gc = cast(GCObject *, upval); + obj.tt = LUA_TUPVAL; + checkliveness(G(L), obj); + + pushObject(luaState, &obj); +} + +void pushString(lua_State *luaState, TString *str) { + TValue o; + setsvalue(luaState, &o, str); + + pushObject(luaState, &o); +} + +/* A simple reimplementation of the unfortunately static function luaA_index. + * Does not support the global table, registry, or upvalues. */ +StkId getObject(lua_State *luaState, int stackpos) { + if (stackpos > 0) { + lua_assert(luaState->base + stackpos - 1 < luaState->top); + return luaState->base + stackpos - 1; + } else { + lua_assert(L->top - stackpos >= L->base); + return luaState->top + stackpos; + } +} + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { + global_State *globalState = G(luaState); + + obj->gch.next = globalState->rootgc; + globalState->rootgc = obj; + obj->gch.marked = luaC_white(globalState); + obj->gch.tt = type; +} + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { + Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); + lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); + + c->l.isC = 0; + c->l.env = elementTable; + c->l.nupvalues = cast_byte(numElements); + + while (numElements--) { + c->l.upvals[numElements] = NULL; + } + + return c; +} + +void pushClosure(lua_State *luaState, Closure *closure) { + TValue obj; + setclvalue(luaState, &obj, closure); + pushObject(luaState, &obj); +} + +Proto *createProto(lua_State *luaState) { + Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); + + newProto->k = NULL; + newProto->sizek = 0; + newProto->p = NULL; + newProto->sizep = 0; + newProto->code = NULL; + newProto->sizecode = 0; + newProto->sizelineinfo = 0; + newProto->sizeupvalues = 0; + newProto->nups = 0; + newProto->upvalues = NULL; + newProto->numparams = 0; + newProto->is_vararg = 0; + newProto->maxstacksize = 0; + newProto->lineinfo = NULL; + newProto->sizelocvars = 0; + newProto->locvars = NULL; + newProto->linedefined = 0; + newProto->lastlinedefined = 0; + newProto->source = NULL; + + return newProto; +} + +TString *createString(lua_State *luaState, const char *str, size_t len) { + TString *res; + lua_pushlstring(luaState, str, len); + + res = rawtsvalue(luaState->top - 1); + lua_pop(luaState, 1); + + return res; +} + +Proto *makeFakeProto(lua_State *L, lu_byte nups) { + Proto *p = createProto(L); + + p->sizelineinfo = 1; + p->lineinfo = lua_newVector(L, 1, int); + p->lineinfo[0] = 1; + p->sizecode = 1; + p->code = lua_newVector(L, 1, Instruction); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->source = createString(L, "", 0); + p->maxstacksize = 2; + p->nups = nups; + p->sizek = 0; + p->sizep = 0; + + return p; +} + +UpVal *createUpValue(lua_State *luaState, int stackpos) { + UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); + lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); + upValue->tt = LUA_TUPVAL; + upValue->v = &upValue->u.value; + upValue->u.l.prev = NULL; + upValue->u.l.next = NULL; + + const TValue *o2 = (TValue *)getObject(luaState, stackpos); + upValue->v->value = o2->value; + upValue->v->tt = o2->tt; + checkliveness(G(L), upValue->v); + + return upValue; +} + +void unboxUpValue(lua_State *luaState) { + // >>>>> ...... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)clvalue(getObject(luaState, -1)); + uv = lcl->upvals[0]; + + lua_pop(luaState, 1); + // >>>>> ...... + + pushUpValue(luaState, uv); + // >>>>> ...... upValue +} + +size_t appendStackToStack_reverse(lua_State *from, lua_State *to) { + for (StkId id = from->top - 1; id >= from->stack; --id) { + setobj2s(to, to->top, id); + to->top++; + } + + return from->top - from->stack; +} + +void correctStack(lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + +void lua_reallocstack(lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + + lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack + newsize; + correctStack(L, oldstack); +} + +void lua_growstack(lua_State *L, int n) { + // Double size is enough? + if (n <= L->stacksize) { + lua_reallocstack(L, 2 * L->stacksize); + } else { + lua_reallocstack(L, L->stacksize + n); + } +} + +void lua_reallocCallInfo(lua_State *lauState, int newsize) { + CallInfo *oldci = lauState->base_ci; + lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); + + lauState->size_ci = newsize; + lauState->ci = (lauState->ci - oldci) + lauState->base_ci; + lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; +} + +void GCUnlink(lua_State *luaState, GCObject *gco) { + GCObject *prevslot; + if (G(luaState)->rootgc == gco) { + G(luaState)->rootgc = G(luaState)->rootgc->gch.next; + return; + } + + prevslot = G(luaState)->rootgc; + while (prevslot->gch.next != gco) { + prevslot = prevslot->gch.next; + } + + prevslot->gch.next = prevslot->gch.next->gch.next; +} + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { + lua_pushlstring(luaState, str, len); + TString *luaStr = &(luaState->top - 1)->value.gc->ts; + + lua_pop(luaState, 1); + + return luaStr; +} + +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { + global_State *g = G(luaState); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + +Proto *lua_newproto(lua_State *luaState) { + Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_link(luaState, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + +UpVal *makeUpValue(lua_State *luaState, int stackPos) { + UpVal *uv = lua_new(luaState, UpVal); + lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); + uv->tt = LUA_TUPVAL; + uv->v = &uv->u.value; + uv->u.l.prev = NULL; + uv->u.l.next = NULL; + + setobj(luaState, uv->v, getObject(luaState, stackPos)); + + return uv; +} + +void boxUpValue_start(lua_State *luaState) { + LClosure *closure; + closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); + pushClosure(luaState, (Closure *)closure); + // >>>>> ...... func + closure->p = makeFakeProto(luaState, 1); + + // Temporarily initialize the upvalue to nil + lua_pushnil(luaState); + closure->upvals[0] = makeUpValue(luaState, -1); + lua_pop(luaState, 1); +} + +void boxUpValue_finish(lua_State *luaState) { + // >>>>> ...... func obj + LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); + + lcl->upvals[0]->u.value = *getObject(luaState, -1); + lua_pop(luaState, 1); + // >>>>> ...... func +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence_util.h b/engines/sword25/util/lua_persistence_util.h new file mode 100644 index 0000000000..4d0085e242 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.h @@ -0,0 +1,122 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distri8buted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#ifndef LUA_PERISTENCE_UTIL_H +#define LUA_PERISTENCE_UTIL_H + + +struct lua_State; + +#include "lua/lobject.h" + +typedef TValue *StkId; + +namespace Lua { + +#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) +#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) +#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) +#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) +#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); + +void pushObject(lua_State *luaState, TValue *obj); +void pushProto(lua_State *luaState, Proto *proto); +void pushUpValue(lua_State *luaState, UpVal *upval); +void pushString(lua_State *luaState, TString *str); + +StkId getObject(lua_State *luaState, int stackpos); + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); + +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); +void pushClosure(lua_State *luaState, Closure *closure); + +Proto *createProto(lua_State *luaState); +Proto *makeFakeProto(lua_State *L, lu_byte nups); + +TString *createString(lua_State *luaState, const char *str, size_t len); + +UpVal *createUpValue(lua_State *luaState, int stackpos); +void unboxUpValue(lua_State *luaState); + +/* Appends one stack to another stack, but the stack is reversed in the process */ +size_t appendStackToStack_reverse(lua_State *from, lua_State *to); +void correctStack(lua_State *L, TValue *oldstack); +void lua_reallocstack(lua_State *L, int newsize); +void lua_growstack(lua_State *L, int n); + +void lua_reallocCallInfo(lua_State *lauState, int newsize); + +/* Does basically the opposite of luaC_link(). + * Right now this function is rather inefficient; it requires traversing the + * entire root GC set in order to find one object. If the GC list were doubly + * linked this would be much easier, but there's no reason for Lua to have + * that. */ +void GCUnlink(lua_State *luaState, GCObject *gco); + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); +Proto *lua_newproto(lua_State *luaState) ; + +UpVal *makeUpValue(lua_State *luaState, int stackPos); +/** + * The GC is not fond of finding upvalues in tables. We get around this + * during persistence using a weakly keyed table, so that the GC doesn't + * bother to mark them. This won't work in unpersisting, however, since + * if we make the values weak they'll be collected (since nothing else + * references them). Our solution, during unpersisting, is to represent + * upvalues as dummy functions, each with one upvalue. + */ +void boxUpValue_start(lua_State *luaState); +void boxUpValue_finish(lua_State *luaState); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp new file mode 100644 index 0000000000..ef0ef31041 --- /dev/null +++ b/engines/sword25/util/lua_unpersist.cpp @@ -0,0 +1,722 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serialization.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +struct UnSerializationInfo { + lua_State *luaState; + Common::ReadStream *readStream; +}; + +static void unpersist(UnSerializationInfo *info); + +static void unpersistBoolean(UnSerializationInfo *info); +static void unpersistNumber(UnSerializationInfo *info); +static void unpersistString(UnSerializationInfo *info); +static void unpersistTable(UnSerializationInfo *info, int index); +static void unpersistFunction(UnSerializationInfo *info, int index); +static void unpersistThread(UnSerializationInfo *info, int index); +static void unpersistProto(UnSerializationInfo *info, int index); +static void unpersistUpValue(UnSerializationInfo *info, int index); +static void unpersistUserData(UnSerializationInfo *info, int index); +static void unpersistPermanent(UnSerializationInfo *info, int index); + + +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream) { + UnSerializationInfo info; + info.luaState = luaState; + info.readStream = readStream; + + // The process starts with the lua stack as follows: + // >>>>> permTbl + // That's the table of permanents + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 3); + + // Create a table to hold indexes of everything thats already been read + lua_newtable(luaState); + // >>>>> permTbl indexTbl + + // Prevent garbage collection while we unserialize + lua_gc(luaState, LUA_GCSTOP, 0); + + // Unserialize the root object + unpersist(&info); + // >>>>> permTbl indexTbl rootObj + + // Re-start garbage collection + lua_gc(luaState, LUA_GCRESTART, 0); + + // Remove the indexTbl + lua_replace(luaState, 2); + // >>>>> permTbl rootObj +} + +/* The object is left on the stack. This is primarily used by unserialize, but + * may be used by GCed objects that may incur cycles in order to preregister + * the object. */ +static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... obj index + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj index obj + + // Push the k/v pair into the indexTbl + lua_settable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj +} + +static void unpersist(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte isARealValue = info->readStream->readByte(); + if (isARealValue) { + int index = info->readStream->readSint32LE(); + int type = info->readStream->readSint32LE(); + + switch (type) { + case LUA_TBOOLEAN: + unpersistBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + unpersistNumber(info); + break; + case LUA_TSTRING: + unpersistString(info); + break; + case LUA_TTABLE: + unpersistTable(info, index); + break; + case LUA_TFUNCTION: + unpersistFunction(info, index); + break; + case LUA_TTHREAD: + unpersistThread(info, index); + break; + case LUA_TPROTO: + unpersistProto(info, index); + break; + case LUA_TUPVAL: + unpersistUpValue(info, index); + break; + case LUA_TUSERDATA: + unpersistUserData(info, index); + break; + case PERMANENT_TYPE: + unpersistPermanent(info, index); + break; + default: + assert(0); + } + + + // >>>>> permTbl indexTbl ...... obj + assert(lua_type(info->luaState, -1) == type || + type == PERMANENT_TYPE || + // Remember, upvalues get a special dispensation, as described in boxUpValue + (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); + + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... obj + } else { + int index = info->readStream->readSint32LE(); + + if (index == 0) { + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... nil + } else { + // Fetch the object from the indexTbl + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... index + + lua_gettable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... ?obj? + + assert(!lua_isnil(info->luaState, -1)); + } + // >>>>> permTbl indexTbl ...... obj/nil + } + + // >>>>> permTbl indexTbl ...... obj/nil +} + +static void unpersistBoolean(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int value = info->readStream->readSint32LE(); + + lua_pushboolean(info->luaState, value); + // >>>>> permTbl indexTbl ...... bool +} + +static void unpersistNumber(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // Read the serialized double + Util::SerializedDouble serializedValue; + serializedValue.significandOne = info->readStream->readUint32LE(); + serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); + serializedValue.exponent = info->readStream->readSint16LE(); + + lua_Number value = Util::decodeDouble(serializedValue); + + lua_pushnumber(info->luaState, value); + // >>>>> permTbl indexTbl ...... num +} + +static void unpersistString(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + uint32 length = info->readStream->readUint32LE(); + char *string = new char[length]; + + info->readStream->read(string, length); + lua_pushlstring(info->luaState, string, length); + + // >>>>> permTbl indexTbl ...... string + + delete[] string; +} + +static void unserializeSpecialTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + unpersist(info); + + // >>>>> permTbl indexTbl ...... spfunc + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void unserializeLiteralTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Preregister table for handling of cycles + lua_newtable(info->luaState); + + // >>>>> permTbl indexTbl ...... tbl + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + + // Unserialize metatable + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? + + if (lua_istable(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl metaTbl + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl + } else { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + } + // >>>>> permTbl indexTbl ...... tbl + + + while (1) { + // >>>>> permTbl indexTbl ...... tbl + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl key/nil + + // The table serialization is nil terminated + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + break; + } + + // >>>>> permTbl indexTbl ...... tbl key + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl value + + lua_rawset(info->luaState, -3); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unpersistTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int isSpecial = info->readStream->readSint32LE(); + + if (isSpecial) { + unserializeSpecialTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } else { + unserializeLiteralTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unpersistFunction(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte numUpValues = info->readStream->readByte(); + + LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); + pushClosure(info->luaState, (Closure *)lclosure); + // >>>>> permTbl indexTbl ...... func + + // Put *some* proto in the closure, before the GC can find it + lclosure->p = makeFakeProto(info->luaState, numUpValues); + + //Also, we need to temporarily fill the upvalues + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + + for (byte i = 0; i < numUpValues; ++i) { + lclosure->upvals[i] = createUpValue(info->luaState, -1); + } + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // I can't see offhand how a function would ever get to be self- + // referential, but just in case let's register it early + registerObjectInIndexTable(info, index); + + // Now that it's safe, we can get the real proto + unpersist(info); + // >>>>> permTbl indexTbl ...... func proto + + lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + for (byte i = 0; i < numUpValues; ++i) { + // >>>>> permTbl indexTbl ...... func + unpersist(info); + // >>>>> permTbl indexTbl ...... func func2 + + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... func upValue + lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // Finally, the fenv + unpersist(info); + + // >>>>> permTbl indexTbl ...... func ?fenv/nil? + if (!lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... func fenv + lua_setfenv(info->luaState, -2); + // >>>>> permTbl indexTbl ...... func + } else { + // >>>>> permTbl indexTbl ...... func nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func +} + +void unpersistThread(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + lua_State *L2; + uint32 stacklimit = 0; + + L2 = lua_newthread(info->luaState); + lua_checkstack(info->luaState, 3); + + // L1: permTbl indexTbl ...... thread + // L2: (empty) + registerObjectInIndexTable(info, index); + + // First, deserialize the object stack + uint32 stackSize = info->readStream->readUint32LE(); + lua_growstack(info->luaState, (int)stackSize); + + // Make sure that the first stack element (a nil, representing + // the imaginary top-level C function) is written to the very, + // very bottom of the stack + L2->top--; + for (uint32 i = 0; i < stackSize; ++i) { + unpersist(info); + // L1: permTbl indexTbl ...... thread obj* + } + + lua_xmove(info->luaState, L2, stackSize); + // L1: permTbl indexTbl ...... thread + // L2: obj* + + // Hereafter, stacks refer to L1 + + + // Now, deserialize the CallInfo stack + + uint32 numFrames = info->readStream->readUint32LE(); + + lua_reallocCallInfo(L2, numFrames * 2); + for (uint32 i = 0; i < numFrames; ++i) { + CallInfo *ci = L2->base_ci + i; + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stackfunc = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + ci->nresults = info->readStream->readSint32LE(); + + uint32 savedpc = info->readStream->readUint32LE(); + + if (stacklimit < stacktop) { + stacklimit = stacktop; + } + + ci->base = L2->stack + stackbase; + ci->func = L2->stack + stackfunc; + ci->top = L2->stack + stacktop; + ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code + savedpc : 0; + ci->tailcalls = 0; + + // Update the pointer each time, to keep the GC happy + L2->ci = ci; + } + + // >>>>> permTbl indexTbl ...... thread + // Deserialize the state's other parameters, with the exception of upval stuff + + L2->savedpc = L2->ci->savedpc; + L2->status = info->readStream->readByte(); + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + + L2->errfunc = info->readStream->readUint32LE(); + + L2->base = L2->stack + stackbase; + L2->top = L2->stack + stacktop; + + // Finally, "reopen" upvalues. See serializeUpVal() for why we do this + UpVal *uv; + GCObject **nextslot = &L2->openupval; + global_State *g = G(L2); + + while (true) { + unpersist(info); + // >>>>> permTbl indexTbl ...... thread upVal/nil + + // The list is terminated by a nil + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... thread nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + break; + } + + // >>>>> permTbl indexTbl ...... thread boxedUpVal + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... thread boxedUpVal + + uv = &(getObject(info->luaState, -1)->value.gc->uv); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + uint32 stackpos = info->readStream->readUint32LE(); + uv->v = L2->stack + stackpos; + + GCUnlink(info->luaState, (GCObject *)uv); + + uv->marked = luaC_white(g); + *nextslot = (GCObject *)uv; + nextslot = &uv->next; + uv->u.l.prev = &G(L2)->uvhead; + uv->u.l.next = G(L2)->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + G(L2)->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + } + *nextslot = NULL; + + // The stack must be valid at least to the highest value among the CallInfos + // 'top' and the values up to there must be filled with 'nil' + lua_checkstack(L2, (int)stacklimit); + for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { + setnilvalue(o); + } +} + +void unpersistProto(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // We have to be careful. The GC expects a lot out of protos. In particular, we need + // to give the function a valid string for its source, and valid code, even before we + // actually read in the real code. + TString *source = lua_newlstr(info->luaState, "", 0); + Proto *p = lua_newproto(info->luaState); + p->source = source; + p->sizecode = 1; + p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->maxstacksize = 2; + p->sizek = 0; + p->sizep = 0; + + lua_checkstack(info->luaState, 2); + + pushProto(info->luaState, p); + // >>>>> permTbl indexTbl ...... proto + + // We don't need to register early, since protos can never ever be + // involved in cyclic references + + // Read in constant references + int sizek = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); + for (int i = 0; i < sizek; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto k + + setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); + p->sizek++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + // Read in sub-proto references + + int sizep = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); + for (int i = 0; i < sizep; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto subproto + + p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; + p->sizep++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + + // Read in code + p->sizecode = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); + info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); + + + /* Read in upvalue names */ + p->sizeupvalues = info->readStream->readSint32LE(); + if (p->sizeupvalues) { + lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); + for (int i = 0; i < p->sizeupvalues; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto str + + p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in local variable infos + p->sizelocvars = info->readStream->readSint32LE(); + if (p->sizelocvars) { + lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); + for (int i = 0; i < p->sizelocvars; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto str + + p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + p->locvars[i].startpc = info->readStream->readSint32LE(); + p->locvars[i].endpc = info->readStream->readSint32LE(); + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in source string + unpersist(info); + // >>>>> permTbl indexTbl ...... proto sourceStr + + p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Read in line numbers + p->sizelineinfo = info->readStream->readSint32LE(); + if (p->sizelineinfo) { + lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); + info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); + } + + + /* Read in linedefined and lastlinedefined */ + p->linedefined = info->readStream->readSint32LE(); + p->lastlinedefined = info->readStream->readSint32LE(); + + // Read in misc values + p->nups = info->readStream->readByte(); + p->numparams = info->readStream->readByte(); + p->is_vararg = info->readStream->readByte(); + p->maxstacksize = info->readStream->readByte(); +} + +void unpersistUpValue(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + lua_checkstack(info->luaState, 2); + + boxUpValue_start(info->luaState); + // >>>>> permTbl indexTbl ...... func + registerObjectInIndexTable(info, index); + + unpersist(info); + // >>>>> permTbl indexTbl ...... func obj + + boxUpValue_finish(info->luaState); + // >>>>> permTbl indexTbl ...... func +} + +void unpersistUserData(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + int isspecial = info->readStream->readSint32LE(); + if (isspecial) { + unpersist(info); + // >>>>> permTbl indexTbl ...... specialFunc + + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... udata + } else { + uint32 length = info->readStream->readUint32LE(); + lua_newuserdata(info->luaState, length); + // >>>>> permTbl indexTbl ...... udata + registerObjectInIndexTable(info, index); + + info->readStream->read(lua_touserdata(info->luaState, -1), length); + + unpersist(info); + // >>>>> permTbl indexTbl ...... udata metaTable/nil + + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... udata + } + // >>>>> permTbl indexTbl ...... udata +} + +void unpersistPermanent(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + unpersist(info); + // >>>>> permTbl indexTbl ...... permKey + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl ...... perm +} + +} // End of namespace Lua diff --git a/engines/sword25/util/pluto/CHANGELOG b/engines/sword25/util/pluto/CHANGELOG deleted file mode 100644 index 1be321f898..0000000000 --- a/engines/sword25/util/pluto/CHANGELOG +++ /dev/null @@ -1,37 +0,0 @@ -$Id$ - --- 2.4 -- -* Changed upval unboxing to allow upvals which contain func-housed cycles -* Added stack checking to all stack-growing functions -* Serialized debug information for functions - --- 2.3 -- -* Added LUALIB_API declaration for luaopen_pluto - --- 2.2 -- -* Rolled all internal Lua dependencies into the Pluto distribution -* Made the unit tests depend on dynamically loading Pluto - --- 2.1 -- -* Various fixes to make the GC happy -* stack size always expanded where necessary -* fixed some memory leaks -* GC disabled during unpersist -* callstack initialized for traversal - -This changelog is maintained as of version 2.0alpha1. -Earlier versions are changelogged on the LuaForge site. - --- 2.0 -- -* Fixed a few format changes to 5.1.3 -* Fixed myriad warnings -* GCC compliance: not incrementing cast results -* Fix for self-referring upvals -* Renamed loading function to work with Lua module system -* Loading tables with __newindex works -* unpersist makes buffer copy - --- 2.0alpha1 -- -* Fixed all outstanding 5.0->5.1 conversion issues -* Made heavier use of size_t in preference to int -* Fixed GC/Upval issue (thanks to Eric Jacobs) diff --git a/engines/sword25/util/pluto/FILEFORMAT b/engines/sword25/util/pluto/FILEFORMAT deleted file mode 100644 index e7716675c7..0000000000 --- a/engines/sword25/util/pluto/FILEFORMAT +++ /dev/null @@ -1,168 +0,0 @@ -$Id$ - -pluto_persist() produces a "hunk" of objects. Here's the file format adhered -to by the function, and expected by pluto_unpersist(). - -As a developer, I feel that where file format information is given it is of -utmost importance that that information precisely and accurately reflects the -actual operation of the application. Therefore, if you find any discrepancy -between this and actual operation, please lambast me thoroughly over email. - -Pseudo-C is used to express the file format. Padding is assumed to be -nonexistent. The keyword "one_of" is used to express a concept similar to -"union", except that its size is the size of the actual datatype chosen. Thus, -objects which contain, directly or indirectly, a one_of, may vary in size. - - -struct Object { - int firstTime; /* Whether this is the first time the object - is being referenced */ - one_of { - RealObject o; /* if firstTime == 1 */ - Reference r; /* if firstTime == 0 */ - }; -}; - -struct Reference { - int ref; /* The index the object was registered with */ -}; - -struct RealObject { - int type; /* The type of the object */ - one_of { - Boolean b; /* If type == LUA_TBOOLEAN */ - LightUserData l; /* If type == LUA_TLIGHTUSERDATA */ - Number n; /* If type == LUA_TNUMBER */ - String s; /* If type == LUA_TSTRING */ - Table t; /* If type == LUA_TTABLE */ - Function f; /* If type == LUA_TFUNCTION */ - Userdata u; /* If type == LUA_TUSERDATA */ - Thread th; /* If type == LUA_TTHREAD */ - Proto p; /* If type == LUA_TPROTO (from lobject.h) */ - Upval uv; /* If type == LUA_TUPVAL (from lobject.h) */ - }; /* The actual object */ -}; - -struct Boolean { - int32 bvalue; /* 0 for false, 1 for true */ -}; - -struct LightUserData { - void* luvalue; /* The actual, literal pointer */ -}; - -struct Number { - lua_Number nvalue; /* The actual number */ -}; - -struct String { - int length; /* The length of the string */ - char str[length]; /* The actual string (not null terminated) */ -}; - -struct Table { - int isspecial; /* 1 if SP is used; 0 otherwise */ - one_of { - Closure c; /* if isspecial == 1; closure to refill the table */ - LiteralTable t; /* if isspecial == 0; literal table info */ - }; -}; - -struct LiteralTable { - Object metatable; /* nil for default metatable */ - Pair p[]; /* key/value pairs */ - Object nil = nil; /* Nil reference to terminate */ -}; - -struct Pair { - Object key; - Object value; -}; - -struct Function { /* Actually a closure */ - lu_byte nups; /* Number of upvalues the function uses */ - Object proto; /* The proto this function uses */ - Object upvals[nups]; /* All upvalues */ - Object fenv; /* The FEnv (nil for the global table) -}; - -struct Upval { - Object obj; /* The object this upval refers to */ -} - -struct Userdata { - int isSpecial; /* 1 for special persistence, 0 for literal - one_of { - LiteralUserdata lu; /* if is_special is 0 */ - SpecialUserdata su; /* if is_special is 1 */ - }; -}; - -struct LiteralUserdata { - Object metatable; /* The metatable (nil for default) */ - int length; /* Size of the data */ - char data[length]; /* The actual data */ -}; - -struct SpecialUserdata { - int length; /* The size of the data */ - Object func; /* The closure used to fill the userdata */ -}; - -struct Thread { - int stacksize; /* The size of the stack filled with objects, - * including the "nil" that is hidden below - * the bottom of the stack visible to C */ - Object stack[stacksize];/* Indices of all stack values, bottom up */ - int callinfosize; /* Number of elements in the CallInfo stack */ - CallInfo callinfostack[callinfosize]; /* The CallInfo stack */ - int base; /* base = L->base - L->stack; */ - int top; /* top = L->top - L->stack; */ - OpenUpval openupvals[]; /* Upvalues to open */ - Object nil = nil; /* To terminate the open upvalues list */ -}; - -struct OpenUpval { - Object upval; /* The upvalue */ - int stackpos; /* The stack position to "reopen" it to */ - -}; - -struct CallInfo { - int base; /* base = ci->base - L->stack; */ - int top; /* top = ci->top - L->stack; */ - int pc; /* pc = ci->pc - proto->code; */ - int state; /* flags used by the CallInfo */ -}; - -struct Proto { - int sizek; /* Number of constants referenced */ - Object k[sizek]; /* Constants referenced */ - int sizep; /* Number of inner Protos referenced */ - Object p[sizep]; /* Inner Protos referenced */ - int sizecode; /* Number of instructions in code */ - Instruction code[sizecode]; /* The proto's code */ - ProtoDebug debuginfo; /* Debug information for the proto */ - lu_byte nups; /* Number of upvalues used */ - lu_byte numparams; /* Number of parameters taken */ - lu_byte is_vararg; /* 1 if function accepts varargs, 0 otherwise */ - lu_byte maxstacksize; /* Size of stack reserved for the function */ -}; - -struct ProtoDebug { - int sizeupvals; /* Number of upvalue names */ - Object upvals; /* Upvalue names */ - int sizelocvars; /* Number of local variable names */ - LocVar[sizelocvars]; /* Local variable names */ - Object source; /* The source code */ - int sizelineinfo; /* Number of opcode-line mappings */ - int lineinfo[sizelineinfo]; /* opcode-line mappings */ - int linedefined; /* Start of line range */ - int lastlinedefined; /* End of line range */ -}; - -struct LocVar { - Object name; /* Name of the local variable */ - int startpc; /* Point where variable is active */ - int endpc; /* Point where variable is dead */ -}; diff --git a/engines/sword25/util/pluto/README b/engines/sword25/util/pluto/README deleted file mode 100644 index 838fce498b..0000000000 --- a/engines/sword25/util/pluto/README +++ /dev/null @@ -1,133 +0,0 @@ -$Id$ - -PLUTO - Heavy duty persistence for Lua - -Pluto is a library which allows users to write arbitrarily large portions -of the "Lua universe" into a flat file, and later read them back into the -same or a different Lua universe. Object references are appropriately -handled, such that the file contains everything needed to recreate the -objects in question. - -Pluto has the following major features: -* Can persist any Lua function -* Can persist threads -* Works with any Lua chunkreader/chunkwriter -* Support for "invariant" permanent objects, of all datatypes -* Can invoke metafunctions for custom persistence of tables and userdata - -Pluto 2.2 requires Lua 5.1.3. If you need to use Pluto with Lua -5.0, please use version 1.2 of Pluto. - -Starting with version 2.2, Pluto no longer depends on the Lua sources. -Instead, it subsumes the required headers into its own codebase. -As a result, it may not work properly with Lua version 5.1.4 or later. - -Pluto may have bugs. Users are advised to define lua_assert in -luaconf.h to something useful when compiling in debug mode, to catch -assertions by Pluto and Lua. - -The Pluto library consists of two public functions. - -int pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) - -This function recursively persists the Lua object in stack position 2 -and all other objects which are directly or indirectly referenced by -it, except those referenced in the permanent object table. The data -is written using the chunk-writer given, and that writer is passed -the arbitrary pointer value ud. - -The Lua stack must contain exactly and only these two items, in order: - -1. A table of permanent objects, that should not be persisted. For each -permanent object, the object itself should be the key, and a unique -object of any type should be the value. Likely candidates for this table -include Lua functions (including those in the Lua libraries) that are -loaded at load-time. It must include all non-persistable objects that -are referenced by the object to be persisted. The table is not modified -by the function. Objects in this table are considered "opaque" and are -not examined or descended into. Objects should not appear in the table -multiple times; the result of doing this is undefined (though probably -harmless). NOTE: If you are planning to persist threads, keep in mind -that all yielded threads have coroutine.yield on the tops of their -stacks. Since it's a C function, it should be put here. For complex -permanents, it may be a good idea to use the __index meta-function of -the permanents table to "search" for permanents. - -2. The single object to be persisted. In many cases, this will be the -global table. For more flexibility, however, it may be something like a -table built for the occasion, with various values to keep track of. The -object may not be nil. - - -int pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) - -This function loads in a Lua object and places it on top of the stack. All -objects directly or indirectly referenced by it are also loaded. - -The Lua stack must contain, as its top value, a table of permanent -objects. This table should be like the permanent object table used when -persisting, but with the key and value of each pair reversed. These -objects are used as substitutes for those referenced in their positions -when persisting, and under most circumstances should be identical objects -to those referenced in the permanents table used for persisting. It's -okay for multiple keys to refer to the same object. - - -RUNNING PLUTO FROM LUA: -It is also possible to invoke pluto from a Lua script. The C function -pluto_open() will register pluto.persist and pluto.unpersist, lua functions -which operate on strings. The first takes a permanents table and a root -object, and returns a string; the second takes a permanents table and a -string, and returns the root object. - -An error will be raised if pluto.persist is called from a thread which is -itself referenced by the root object. - -SPECIAL PERSISTENCE: -Tables and userdata have special persistence semantics. These semantics are -keyed to the value of the object's metatable's __persist member, if any. This -member may be any of the following four values: -1. Boolean "true": The table or userdata is persisted literally; tables are -persisted member-by-member, and userdata are written out as literal data. -2. Boolean "false": An error is returned, indicating that the object cannot -be persisted. -3. A function: This function should take one argument, the object in question, -and return one result, a closure. This "fixup closure", in turn, will be -persisted, and during unpersistence will be called. The closure will be -responsible for recreating the object with the appropriate data, based on -its upvalues. -4. Nil, or no metatable. In the case of tables, the table is literally -persisted. In the case of userdata, an error is returned. - -Here's an example of special persistence for a simple 3d vector object: - -vec = { x = 2, y = 1, z = 4 } -setmetatable(vec, { __persist = function(oldtbl) - local x = oldtbl.x - local y = oldtbl.y - local z = oldtbl.z - local mt = getmetatable(oldtbl) - return function() - newtbl = {} - newtbl.x = x - newtbl.y = y - newtbl.z = z - setmetatable(newtbl, mt) - return newtbl - end -end }) - -Note how x, y, z, and the mt are explicitly pulled out of the table. It is -important that the fixup closure returned not reference the original table -directly, as that table would again be persisted as an upvalue, leading to an -infinite loop. Also note that the object's metatable is NOT automatically -persisted; it is necessary for the fixup closure to reset it, if it wants. - -LIMITATIONS/TODO: -* Light userdata are persisted literally, as their pointer values. This -may or may not be what you want. -* Closures of C functions may not be persisted. Once it becomes possible -to specify a C function "proto" as a permanent object, this restriction -will be relaxed. - -BUGS: None known. Emphasis on the 'known'. diff --git a/engines/sword25/util/pluto/THANKS b/engines/sword25/util/pluto/THANKS deleted file mode 100644 index 443713fa61..0000000000 --- a/engines/sword25/util/pluto/THANKS +++ /dev/null @@ -1,9 +0,0 @@ -Pluto is surprisingly robust and useful. This would not be the case without -the hard work and helpfulness of the following people, mentioned in no -particular order: - -Ivko Stanilov -Goran Adrinek -Eric Jacobs -Anolan Milanes -Malte Thiesen diff --git a/engines/sword25/util/pluto/pdep.cpp b/engines/sword25/util/pluto/pdep.cpp deleted file mode 100644 index a32c43b42d..0000000000 --- a/engines/sword25/util/pluto/pdep.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is derived from the Lua source code. Please see lua.h for -the copyright statement. -*/ - -#include "pdep/pdep.h" - -#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} - -void pdep_pushobject (lua_State *L, const TValue *o) { - setobj2s(L, L->top, o); - api_incr_top(L); -} - -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { - global_State *g = G(L); - lua_assert((osize == 0) == (block == NULL)); - block = (*g->frealloc)(g->ud, block, osize, nsize); - lua_assert((nsize == 0) == (block == NULL)); - g->totalbytes = (g->totalbytes - osize) + nsize; - return block; -} - -void pdep_link (lua_State *L, GCObject *o, lu_byte tt) { - global_State *g = G(L); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *pdep_newproto (lua_State *L) { - Proto *f = pdep_new(L, Proto); - pdep_link(L, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e) { - Closure *c = cast(Closure *, pdep_malloc(L, sizeLclosure(nelems))); - pdep_link(L, obj2gco(c), LUA_TFUNCTION); - c->l.isC = 0; - c->l.env = e; - c->l.nupvalues = cast_byte(nelems); - while (nelems--) c->l.upvals[nelems] = NULL; - return c; -} - -static void correctstack (lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - - -void pdep_reallocstack (lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); - pdep_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack+newsize; - correctstack(L, oldstack); -} - -void pdep_growstack (lua_State *L, int n) { - if (n <= L->stacksize) /* double size is enough? */ - pdep_reallocstack(L, 2*L->stacksize); - else - pdep_reallocstack(L, L->stacksize + n); -} - -void pdep_reallocCI (lua_State *L, int newsize) { - CallInfo *oldci = L->base_ci; - pdep_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); - L->size_ci = newsize; - L->ci = (L->ci - oldci) + L->base_ci; - L->end_ci = L->base_ci + L->size_ci - 1; -} - -TString *pdep_newlstr (lua_State *L, const char *str, size_t l) { - TString *res; - lua_pushlstring(L, str, l); - res = rawtsvalue(L->top-1); - lua_pop(L, 1); - return res; -} diff --git a/engines/sword25/util/pluto/pdep/README b/engines/sword25/util/pluto/pdep/README deleted file mode 100644 index 3592754da0..0000000000 --- a/engines/sword25/util/pluto/pdep/README +++ /dev/null @@ -1,5 +0,0 @@ -These files are directly copied from the Lua distribution, with the -exception of lzio.h, which is s/lua{ZM}/pdep/g and has an include removed. - -As such, unlike the rest of Pluto, they are released under the -same terms as Lua. See "lua.h" for the copyright notice. diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h deleted file mode 100644 index 2e37f8d202..0000000000 --- a/engines/sword25/util/pluto/pdep/lzio.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -** $Id$ -** Buffered streams -** See Copyright Notice in lua.h -*/ - - -#ifndef lzio_h -#define lzio_h - -#include "sword25/util/lua/lua.h" - - -#define EOZ (-1) /* end of stream */ - -typedef struct Zio ZIO; - -#define char2int(c) cast(int, cast(unsigned char, (c))) - -#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : pdep_fill(z)) - -typedef struct Mbuffer { - char *buffer; - size_t n; - size_t buffsize; -} Mbuffer; - -#define pdep_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) - -#define pdep_buffer(buff) ((buff)->buffer) -#define pdep_sizebuffer(buff) ((buff)->buffsize) -#define pdep_bufflen(buff) ((buff)->n) - -#define pdep_resetbuffer(buff) ((buff)->n = 0) - - -#define pdep_resizebuffer(L, buff, size) \ - (pdep_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ - (buff)->buffsize = size) - -#define pdep_freebuffer(L, buff) pdep_resizebuffer(L, buff, 0) - - -LUAI_FUNC char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n); -LUAI_FUNC void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, - void *data); -LUAI_FUNC size_t pdep_read (ZIO* z, void* b, size_t n); /* read next n bytes */ -LUAI_FUNC int pdep_lookahead (ZIO *z); - - - -/* --------- Private Part ------------------ */ - -struct Zio { - size_t n; /* bytes still unread */ - const char *p; /* current position in buffer */ - lua_Reader reader; - void* data; /* additional data */ - lua_State *L; /* Lua state (for reader) */ -}; - - -LUAI_FUNC int pdep_fill (ZIO *z); - -#endif diff --git a/engines/sword25/util/pluto/pdep/pdep.h b/engines/sword25/util/pluto/pdep/pdep.h deleted file mode 100644 index 664fc812b5..0000000000 --- a/engines/sword25/util/pluto/pdep/pdep.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef PDEP_H -#define PDEP_H - -#include "sword25/util/lua/lua.h" -#include "sword25/util/pluto/pdep/lzio.h" -#include "sword25/util/lua/ldo.h" -#include "sword25/util/lua/lfunc.h" -#include "sword25/util/lua/lgc.h" -#include "sword25/util/lua/llimits.h" -#include "sword25/util/lua/lobject.h" -#include "sword25/util/lua/lopcodes.h" -#include "sword25/util/lua/lstate.h" -#include "sword25/util/lua/lstring.h" -#include "sword25/util/lua/lauxlib.h" - - -#define pdep_reallocv(L,b,on,n,e) \ - pdep_realloc_(L, (b), (on)*(e), (n)*(e)) -#define pdep_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, pdep_reallocv(L, v, oldn, n, sizeof(t)))) -#define pdep_freearray(L, b, n, t) pdep_reallocv(L, (b), n, 0, sizeof(t)) -#define pdep_newvector(L,n,t) \ - cast(t *, pdep_reallocv(L, NULL, 0, n, sizeof(t))) -#define pdep_new(L,t) cast(t *, pdep_malloc(L, sizeof(t))) -#define pdep_malloc(L,t) pdep_realloc_(L, NULL, 0, (t)) -#define pdep_checkstack(L,n) \ - if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ - pdep_growstack(L, n); \ - else pdep_reallocstack(L, L->stacksize - EXTRA_STACK - 1); - - -void pdep_pushobject (lua_State *L, const TValue *o); -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize); -void pdep_link (lua_State *L, GCObject *o, lu_byte tt); -Proto *pdep_newproto (lua_State *L); -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e); -void pdep_reallocstack (lua_State *L, int newsize); -void pdep_growstack (lua_State *L, int n); -void pdep_reallocCI (lua_State *L, int newsize); -TString *pdep_newlstr (lua_State *L, const char *str, size_t l); - -#endif diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp deleted file mode 100644 index cbe16b0d5b..0000000000 --- a/engines/sword25/util/pluto/pluto.cpp +++ /dev/null @@ -1,2083 +0,0 @@ -/* $Id$ */ - -/* Tamed Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Instrumented by Stefan Reich (info@luaos.net) - * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) - */ - -#include "sword25/util/lua/lua.h" -#include "pluto.h" - -#undef TOTEXT - -#define USE_PDEP - -#ifdef USE_PDEP -#include "pdep/pdep.h" -#define LIF(prefix, name) pdep ## _ ## name -#else -#include "lapi.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "llimits.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lstate.h" -#include "lstring.h" -#include "lauxlib.h" -#define LIF(prefix, name) lua ## prefix ## _ ## name -#endif - -#include <string.h> - - -/* Define this if you want size_t values to be written in 64-bit - (even on 32-bit systems). Should eliminate at least one source of - 32/64 bit incompatibility. */ -#define SIZES64 - - -/* #define PLUTO_DEBUG */ - - -#ifdef SIZES64 -#define VERSION "Tamed Pluto 1.0 with SIZES64 flag" -#else -#define VERSION "Tamed Pluto 1.0" -#endif - - -#ifdef PLUTO_DEBUG -#include <stdio.h> -#endif - -#define PLUTO_TPERMANENT 101 - -#define verify(x) { int v = (int)((x)); v=v; lua_assert(v); } - -#define NUMTYPES 9 -static const char* typenames[] = { - "nil", - "boolean", - "lightuserdata", - "number", - "string", - "table", - "function", - "userdata", - "thread" -}; - -static int humanReadable = 0; -#define hrBufSize 200 -static char hrBuf[hrBufSize]; - -typedef struct PersistInfo_t { - lua_State *L; - int counter; - lua_Chunkwriter writer; - void *ud; -#ifdef PLUTO_DEBUG - int level; -#endif -} PersistInfo; - -#ifdef PLUTO_DEBUG -void printindent(int indent) -{ - int il; - for(il=0; il<indent; il++) { - printf(" "); - } -} -#endif - -/* lua_Chunkwriter signature: (lua_State *L, const void *p, size_t sz, void *ud). - ud is a pointer to the WriterInfo struct (holds the buffer pointer) -*/ - -static void pi_write(PersistInfo *pi, const void *p, size_t size, void *ud) { - if (humanReadable) { - uint i; - snprintf(hrBuf, hrBufSize, " pi_write %d ", (int) size); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - snprintf(hrBuf, hrBufSize, "%X%X", (b >> 4) & 0xF, b & 0xF); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } - snprintf(hrBuf, hrBufSize, "\n"); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } else { - pi->writer(pi->L, p, size, ud); - } -#ifdef TOTEXT - int i; - printf(" pi_write %d ", (int) size); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - printf("%X%X", (b >> 4) & 0xF, b & 0xF); - } - printf("\n"); -#endif -} - -static void hrOut(PersistInfo *pi) { - pi->writer(pi->L, hrBuf, strlen(hrBuf), pi->ud); -} - -static void write_size(PersistInfo *pi, size_t *val) -{ -#ifdef SIZES64 - int64 longval; /* yeah, you really need long long to get 8 bytes on win32... duh. */ - longval = *val; - pi_write(pi, &longval, 8, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size64 %ld\n", longval); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size64 %ld\n", longval); -#endif -#else - pi_write(pi, val, sizeof(size_t), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size %ld\n", *((size_t *)val)); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size %ld\n", *val); -#endif -#endif -} - -static void read_size(ZIO *zio, size_t *val) -{ -#ifdef SIZES64 - int64 longval; - verify(LIF(Z,read)(zio, &longval, 8) == 0); - *val = longval; -#else - verify(LIF(Z,read)(zio, val, sizeof(size_t)) == 0); -#endif -} - - -/* Mutual recursion requires prototype */ -static void persist(PersistInfo *pi); - -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -static StkId getobject(lua_State *L, int stackpos) -{ - if(stackpos > 0) { - lua_assert(L->base+stackpos-1 < L->top); - return L->base+stackpos-1; - } else { - lua_assert(L->top-stackpos >= L->base); - return L->top+stackpos; - } -} - -/* Choose whether to do a regular or special persistence based on an object's - * metatable. "default" is whether the object, if it doesn't have a __persist - * entry, is literally persistable or not. - * Pushes the unpersist closure and returns true if special persistence is - * used. */ -static int persistspecialobject(PersistInfo *pi, int defaction) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 4); - /* Check whether we should persist literally, or via the __persist - * metafunction */ - if(!lua_getmetatable(pi->L, -1)) { - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - } - } - /* perms reftbl sptbl ... obj mt */ - lua_pushstring(pi->L, "__persist"); - /* perms reftbl sptbl ... obj mt "__persist" */ - lua_rawget(pi->L, -2); - /* perms reftbl sptbl ... obj mt __persist? */ - if(lua_isnil(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt nil */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero2\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero2\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(lua_isboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt bool */ - if(lua_toboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt true */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero3\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero3\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Metatable forbade persistence"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist not nil, boolean, or function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt __persist */ - lua_pushvalue(pi->L, -3); - /* perms reftbl ... obj mt __persist obj */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(pi->L, (void *)pi->writer); - lua_pushlightuserdata(pi->L, pi->ud); - /* perms reftbl ... obj mt __persist obj ud */ - lua_call(pi->L, 3, 1); - /* perms reftbl ... obj mt func? */ -#else - lua_call(pi->L, 1, 1); - /* perms reftbl ... obj mt func? */ -#endif - /* perms reftbl ... obj mt func? */ - if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist function did not return a function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt func */ - { - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_one\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_one\n"); -#endif - } - persist(pi); - /* perms reftbl ... obj mt func */ - lua_pop(pi->L, 2); - /* perms reftbl ... obj */ - return 1; -} - -static void persisttable(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persisttable\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persisttable\n"); -#endif - - /* perms reftbl ... tbl */ - lua_checkstack(pi->L, 3); - if(persistspecialobject(pi, 1)) { - /* perms reftbl ... tbl */ - return; - } - /* perms reftbl ... tbl */ - /* First, persist the metatable (if any) */ - if(!lua_getmetatable(pi->L, -1)) { - lua_pushnil(pi->L); - } - /* perms reftbl ... tbl mt/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ - - /* Now, persist all k/v pairs */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - while(lua_next(pi->L, -2)) { - /* perms reftbl ... tbl k v */ - lua_pushvalue(pi->L, -2); - /* perms reftbl ... tbl k v k */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k v */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k */ - } - /* perms reftbl ... tbl */ - /* Terminate list */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ -} - -static void persistuserdata(PersistInfo *pi) { - /* perms reftbl ... udata */ - lua_checkstack(pi->L, 2); - if(persistspecialobject(pi, 0)) { - /* perms reftbl ... udata */ - return; - } else { - /* Use literal persistence */ - size_t length = uvalue(getobject(pi->L, -1))->len; - write_size(pi, &length); - pi_write(pi, lua_touserdata(pi->L, -1), length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistuserdata %ld\n", (long) length); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistuserdata %ld\n", (long) length); -#endif - if(!lua_getmetatable(pi->L, -1)) { - /* perms reftbl ... udata */ - lua_pushnil(pi->L); - /* perms reftbl ... udata mt/nil */ - } - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... udata */ - } -} - - -static Proto *toproto(lua_State *L, int stackpos) -{ - return gco2p(getobject(L, stackpos)->value.gc); -} - -static UpVal *toupval(lua_State *L, int stackpos) -{ - lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL); - return gco2uv(getobject(L, stackpos)->value.gc); -} - -static void pushproto(lua_State *L, Proto *proto) -{ - TValue o; - setptvalue(L, &o, proto); - LIF(A,pushobject)(L, &o); -} - -#define setuvvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \ - checkliveness(G(L),i_o); } - -static void pushupval(lua_State *L, UpVal *upval) -{ - TValue o; - setuvvalue(L, &o, upval); - LIF(A,pushobject)(L, &o); -} - -static void pushclosure(lua_State *L, Closure *closure) -{ - TValue o; - setclvalue(L, &o, closure); - LIF(A,pushobject)(L, &o); -} - -static void pushstring(lua_State *L, TString *s) -{ - TValue o; - setsvalue(L, &o, s); - LIF(A,pushobject)(L, &o); -} - -static void persistfunction(PersistInfo *pi) -{ - /* perms reftbl ... func */ - Closure *cl = clvalue(getobject(pi->L, -1)); - lua_checkstack(pi->L, 2); - if(cl->c.isC) { - /* It's a C function. For now, we aren't going to allow - * persistence of C closures, even if the "C proto" is - * already in the permanents table. */ - lua_pushstring(pi->L, "Attempt to persist a C function"); - lua_error(pi->L); - } else { - /* It's a Lua closure. */ - { - /* We don't really _NEED_ the number of upvals, - * but it'll simplify things a bit */ - pi_write(pi, &cl->l.p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); -#endif - } - /* Persist prototype */ - { - pushproto(pi->L, cl->l.p); - /* perms reftbl ... func proto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* Persist upvalue values (not the upvalue objects - * themselves) */ - { - int i; - for(i=0; i<cl->l.p->nups; i++) { - /* perms reftbl ... func */ - pushupval(pi->L, cl->l.upvals[i]); - /* perms reftbl ... func upval */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - } - /* Persist function environment */ - { - lua_getfenv(pi->L, -1); - /* perms reftbl ... func fenv */ - if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) { - /* Function has the default fenv */ - /* perms reftbl ... func _G */ - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - lua_pushnil(pi->L); - /* perms reftbl ... func nil */ - } - /* perms reftbl ... func fenv/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - } -} - - -/* Upvalues are tricky. Here's why. - * - * A particular upvalue may be either "open", in which case its member v - * points into a thread's stack, or "closed" in which case it points to the - * upvalue itself. An upvalue is closed under any of the following conditions: - * -- The function that initially declared the variable "local" returns - * -- The thread in which the closure was created is garbage collected - * - * To make things wackier, just because a thread is reachable by Lua doesn't - * mean it's in our root set. We need to be able to treat an open upvalue - * from an unreachable thread as a closed upvalue. - * - * The solution: - * (a) For the purposes of persisting, don't indicate whether an upvalue is - * closed or not. - * (b) When unpersisting, pretend that all upvalues are closed. - * (c) When persisting, persist all open upvalues referenced by a thread - * that is persisted, and tag each one with the corresponding stack position - * (d) When unpersisting, "reopen" each of these upvalues as the thread is - * unpersisted - */ -static void persistupval(PersistInfo *pi) -{ - /* perms reftbl ... upval */ - UpVal *uv = toupval(pi->L, -1); - lua_checkstack(pi->L, 1); - - /* We can't permit the upval to linger around on the stack, as Lua - * will bail if its GC finds it. */ - - lua_pop(pi->L, 1); - /* perms reftbl ... */ - LIF(A,pushobject)(pi->L, uv->v); - /* perms reftbl ... obj */ - persist(pi); - /* perms reftbl ... obj */ -} - -static void persistproto(PersistInfo *pi) -{ - /* perms reftbl ... proto */ - Proto *p = toproto(pi->L, -1); - lua_checkstack(pi->L, 2); - - /* Persist constant refs */ - { - int i; - pi_write(pi, &p->sizek, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizek %d\n", p->sizek); - hrOut(pi); - } - #ifdef TOTEXT - printf("persistproto_sizek %d\n", p->sizek); - #endif - for(i=0; i<p->sizek; i++) { - LIF(A,pushobject)(pi->L, &p->k[i]); - /* perms reftbl ... proto const */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* serialize inner Proto refs */ - { - int i; - pi_write(pi, &p->sizep, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizep %d\n", p->sizep); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizep %d\n", p->sizep); -#endif - for(i=0; i<p->sizep; i++) - { - pushproto(pi->L, p->p[i]); - /* perms reftbl ... proto subproto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* Serialize code */ - { - int len; - pi_write(pi, &p->sizecode, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizecode %d\n", p->sizecode); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizecode %d\n", p->sizecode); -#endif - len = sizeof(Instruction) * p->sizecode; - pi_write(pi, p->code, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_code %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_code %d\n", len); -#endif - } - - /* Serialize upvalue names */ - { - int i; - pi_write(pi, &p->sizeupvalues, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_upvalues %d\n", p->sizeupvalues); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_upvalues %d\n", p->sizeupvalues); -#endif - for(i=0; i<p->sizeupvalues; i++) - { - pushstring(pi->L, p->upvalues[i]); - persist(pi); - lua_pop(pi->L, 1); - } - } - /* Serialize local variable infos */ - { - int i; - pi_write(pi, &p->sizelocvars, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelocvars %d\n", p->sizelocvars); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelocvars %d\n", p->sizelocvars); -#endif - for(i=0; i<p->sizelocvars; i++) - { - pushstring(pi->L, p->locvars[i].varname); - persist(pi); - lua_pop(pi->L, 1); - - pi_write(pi, &p->locvars[i].startpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_startpc %d\n", p->locvars[i].startpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_startpc %d\n", p->locvars[i].startpc); -#endif - pi_write(pi, &p->locvars[i].endpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_endpc %d\n", p->locvars[i].endpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_endpc %d\n", p->locvars[i].endpc); -#endif - } - } - - /* Serialize source string */ - pushstring(pi->L, p->source); - persist(pi); - lua_pop(pi->L, 1); - - /* Serialize line numbers */ - { - pi_write(pi, &p->sizelineinfo, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelineinfo %d\n", p->sizelineinfo); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelineinfo %d\n", p->sizelineinfo); -#endif - if (p->sizelineinfo) - { - int len; - len = sizeof(int) * p->sizelineinfo; - pi_write(pi, p->lineinfo, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lineinfo %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lineinfo %d\n", len); -#endif - } - } - - /* Serialize linedefined and lastlinedefined */ - pi_write(pi, &p->linedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_linedefined %d\n", p->linedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_linedefined %d\n", p->linedefined); -#endif - pi_write(pi, &p->lastlinedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lastlinedefined %d\n", p->lastlinedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lastlinedefined %d\n", p->lastlinedefined); -#endif - - /* Serialize misc values */ - { - pi_write(pi, &p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_nups %d\n", (int) p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_nups %d\n", (int) p->nups); -#endif - pi_write(pi, &p->numparams, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_numparams %d\n", (int) p->numparams); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_numparams %d\n", (int) p->numparams); -#endif - pi_write(pi, &p->is_vararg, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_is_vararg %d\n", (int) p->is_vararg); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_is_vararg %d\n", (int) p->is_vararg); -#endif - pi_write(pi, &p->maxstacksize, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_maxstacksize %d\n", (int) p->maxstacksize); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_maxstacksize %d\n", (int) p->maxstacksize); -#endif - } - /* We do not currently persist upvalue names, local variable names, - * variable lifetimes, line info, or source code. */ -} - -/* Copies a stack, but the stack is reversed in the process - */ -static size_t revappendstack(lua_State *from, lua_State *to) -{ - StkId o; - for(o=from->top-1; o>=from->stack; o--) { - setobj2s(to, to->top, o); - to->top++; - } - return from->top - from->stack; -} - -/* Persist all stack members - */ -static void persistthread(PersistInfo *pi) -{ - size_t posremaining; - lua_State *L2; - /* perms reftbl ... thr */ - L2 = lua_tothread(pi->L, -1); - lua_checkstack(pi->L, L2->top - L2->stack + 1); - if(pi->L == L2) { - lua_pushstring(pi->L, "Can't persist currently running thread"); - lua_error(pi->L); - return; /* not reached */ - } - - /* Persist the stack */ - posremaining = revappendstack(L2, pi->L); - /* perms reftbl ... thr (rev'ed contents of L2) */ - write_size(pi, &posremaining); - for(; posremaining > 0; posremaining--) { - persist(pi); - lua_pop(pi->L, 1); - } - /* perms reftbl ... thr */ - /* Now, persist the CallInfo stack. */ - { - size_t i, numframes = (L2->ci - L2->base_ci) + 1; - write_size(pi, &numframes); - for(i=0; i<numframes; i++) { - CallInfo *ci = L2->base_ci + i; - size_t stackbase = ci->base - L2->stack; - size_t stackfunc = ci->func - L2->stack; - size_t stacktop = ci->top - L2->stack; - size_t savedpc = (ci != L2->base_ci) ? - ci->savedpc - ci_func(ci)->l.p->code : - 0; - write_size(pi, &stackbase); - write_size(pi, &stackfunc); - write_size(pi, &stacktop); - pi_write(pi, &ci->nresults, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread %d\n", ci->nresults); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread %d\n", ci->nresults); -#endif - write_size(pi, &savedpc); - } - } - - /* Serialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase = L2->base - L2->stack; - size_t stacktop = L2->top - L2->stack; - lua_assert(L2->nCcalls <= 1); - pi_write(pi, &L2->status, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread_status %d\n", (int) L2->status); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread_status %d\n", (int) L2->status); -#endif - write_size(pi, &stackbase); - write_size(pi, &stacktop); - - // ptrdiff_t changes sizes based on 32/64 bit - // Hard cast to 64 bit size if SIZE64 is defined -#ifdef SIZES64 - uint64 ptrIndex = static_cast<uint64>(L2->errfunc); - pi_write(pi, &ptrIndex, sizeof(uint64), pi->ud); -#else - pi_write(pi, &L2->errfunc, sizeof(ptrdiff_t), pi->ud); -#endif - //write_size(pi, (size_t *)&L2->errfunc); - } - - /* Finally, record upvalues which need to be reopened */ - /* See the comment above persistupval() for why we do this */ - { - GCObject *gco; - UpVal *uv; - /* perms reftbl ... thr */ - for(gco = L2->openupval; gco != NULL; gco = uv->next) { - size_t stackpos; - uv = gco2uv(gco); - - /* Make sure upvalue is really open */ - lua_assert(uv->v != &uv->u.value); - pushupval(pi->L, uv); - /* perms reftbl ... thr uv */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - stackpos = uv->v - L2->stack; - write_size(pi, &stackpos); - } - /* perms reftbl ... thr */ - lua_pushnil(pi->L); - /* perms reftbl ... thr nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - } - /* perms reftbl ... thr */ -} - -static void persistboolean(PersistInfo *pi) -{ - int b = lua_toboolean(pi->L, -1); - pi_write(pi, &b, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistboolean %d\n", b); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistboolean %d\n", b); -#endif -} - -static void persistlightuserdata(PersistInfo *pi) -{ - void *p = lua_touserdata(pi->L, -1); - pi_write(pi, &p, sizeof(void *), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistlightuserdata %p\n", p); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistlightuserdata %d\n", (int) p); -#endif -} - -static void persistnumber(PersistInfo *pi) -{ - lua_Number n = lua_tonumber(pi->L, -1); - pi_write(pi, &n, sizeof(lua_Number), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); -#endif -} - -static void persiststring(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring\n"); -#endif - size_t length = lua_strlen(pi->L, -1); - write_size(pi, &length); - const char* s = lua_tostring(pi->L, -1); - pi_write(pi, s, length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring %d \"%s\"\n", (int)length, s); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring %d \"%s\"\n", length, s); -#endif -} - -/* Top-level delegating persist function - */ -static void persist(PersistInfo *pi) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 2); - /* If the object has already been written, write a reference to it */ - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_rawget(pi->L, 2); - /* perms reftbl ... obj ref? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj ref */ - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_seenobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_seenobject\n"); -#endif - int *ref = (int *)lua_touserdata(pi->L, -1); - pi_write(pi, ref, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_touserdata_ref %d\n", ref); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_touserdata_ref %d\n", ref); -#endif - lua_pop(pi->L, 1); - /* perms reftbl ... obj ref */ -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 %d\n", ref); -#endif - return; - } - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - /* If the object is nil, write the pseudoreference 0 */ - if(lua_isnil(pi->L, -1)) { - int zero = 0; - /* firsttime */ - pi_write(pi, &zero, sizeof(int), pi->ud); - /* ref */ - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_nil (last 2 lines)\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_nil (last 2 lines)\n"); -#endif -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 0\n"); -#endif - return; - } - { - /* indicate that it's the first time */ - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_newobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_newobject\n"); -#endif - } - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - int *ref = (int *)lua_newuserdata(pi->L, sizeof(int)); - *ref = ++(pi->counter); - /* perms reftbl ... obj obj ref */ - lua_rawset(pi->L, 2); - /* perms reftbl ... obj */ - - pi_write(pi, &pi->counter, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_counter %d\n", pi->counter); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_counter %d\n", pi->counter); -#endif - - - /* At this point, we'll give the permanents table a chance to play. */ - { - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_gettable(pi->L, 1); - /* perms reftbl ... obj permkey? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj permkey */ - int type = PLUTO_TPERMANENT; -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d PERM\n", pi->counter); - pi->level++; -#endif - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_permtype %d\n", type); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_permtype %d\n", type); -#endif - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - pi->level--; -#endif - return; - } else { - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - } - /* perms reftbl ... obj */ - } - { - int type = lua_type(pi->L, -1); - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); -#endif - -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d %d\n", pi->counter, type); - pi->level++; -#endif - } - - switch(lua_type(pi->L, -1)) { - case LUA_TBOOLEAN: - persistboolean(pi); - break; - case LUA_TLIGHTUSERDATA: - persistlightuserdata(pi); - break; - case LUA_TNUMBER: - persistnumber(pi); - break; - case LUA_TSTRING: - persiststring(pi); - break; - case LUA_TTABLE: - persisttable(pi); - break; - case LUA_TFUNCTION: - persistfunction(pi); - break; - case LUA_TTHREAD: - persistthread(pi); - break; - case LUA_TPROTO: - persistproto(pi); - break; - case LUA_TUPVAL: - persistupval(pi); - break; - case LUA_TUSERDATA: - persistuserdata(pi); - break; - default: - lua_assert(0); - } -#ifdef PLUTO_DEBUG - pi->level--; -#endif -} - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) -{ - PersistInfo pi; - - pi.counter = 0; - pi.L = L; - pi.writer = writer; - pi.ud = ud; -#ifdef PLUTO_DEBUG - pi.level = 0; -#endif - - lua_checkstack(L, 4); - /* perms? rootobj? ...? */ - lua_assert(lua_gettop(L) == 2); - /* perms rootobj */ - lua_assert(!lua_isnil(L, 2)); - /* perms rootobj */ - lua_newtable(L); - /* perms rootobj reftbl */ - - /* Now we're going to make the table weakly keyed. This prevents the - * GC from visiting it and trying to mark things it doesn't want to - * mark in tables, e.g. upvalues. All objects in the table are - * a priori reachable, so it doesn't matter that we do this. */ - lua_newtable(L); - /* perms rootobj reftbl mt */ - lua_pushstring(L, "__mode"); - /* perms rootobj reftbl mt "__mode" */ - lua_pushstring(L, "k"); - /* perms rootobj reftbl mt "__mode" "k" */ - lua_settable(L, 4); - /* perms rootobj reftbl mt */ - lua_setmetatable(L, 3); - /* perms rootobj reftbl */ - lua_insert(L, 2); - /* perms reftbl rootobj */ - persist(&pi); - /* perms reftbl rootobj */ - lua_remove(L, 2); - /* perms rootobj */ -} - -typedef struct WriterInfo_t { - char* buf; - size_t buflen; -} WriterInfo; - -static int bufwriter (lua_State *L, const void *p, size_t sz, void *ud) { - const char *cp = (const char *)p; - WriterInfo *wi = (WriterInfo *)ud; - - LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char); - while(sz) - { - /* how dearly I love ugly C pointer twiddling */ - wi->buf[wi->buflen++] = *cp++; - sz--; - } - return 0; -} - -int persist_l(lua_State *L) -{ - /* perms? rootobj? ...? */ - WriterInfo wi; - - wi.buf = NULL; - wi.buflen = 0; - - lua_settop(L, 2); - /* perms? rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj */ - - pluto_persist(L, bufwriter, &wi); - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, wi.buf, wi.buflen); - /* str */ - pdep_freearray(L, wi.buf, wi.buflen, char); - return 1; -} - -typedef struct UnpersistInfo_t { - lua_State *L; - ZIO zio; -#ifdef PLUTO_DEBUG - int level; -#endif -} UnpersistInfo; - -static void unpersist(UnpersistInfo *upi); - -/* The object is left on the stack. This is primarily used by unpersist, but - * may be used by GCed objects that may incur cycles in order to preregister - * the object. */ -static void registerobject(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... obj */ - lua_checkstack(upi->L, 2); - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... obj ref */ - lua_pushvalue(upi->L, -2); - /* perms reftbl ... obj ref obj */ - lua_settable(upi->L, 2); - /* perms reftbl ... obj */ -} - -static void unpersistboolean(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int b; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0); - lua_pushboolean(upi->L, b); - /* perms reftbl ... bool */ -} - -static void unpersistlightuserdata(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - void *p; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0); - lua_pushlightuserdata(upi->L, p); - /* perms reftbl ... ludata */ -} - -static void unpersistnumber(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_Number n; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0); - lua_pushnumber(upi->L, n); - /* perms reftbl ... num */ -} - -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);*/ - read_size(&upi->zio, &length); - string = pdep_newvector(upi->L, length, char); - verify(LIF(Z,read)(&upi->zio, string, length) == 0); - lua_pushlstring(upi->L, string, length); - /* perms reftbl sptbl ref str */ - pdep_freearray(upi->L, string, length, char); -} - -static void unpersistspecialtable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ - lua_call(upi->L, 0, 1); - /* perms reftbl ... tbl? */ - lua_assert(lua_istable(upi->L, -1)); - /* perms reftbl ... tbl */ -} - -static void unpersistliteraltable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 3); - /* Preregister table for handling of cycles */ - lua_newtable(upi->L); - /* perms reftbl ... tbl */ - registerobject(ref, upi); - /* perms reftbl ... tbl */ - /* Unpersist metatable */ - { - unpersist(upi); - /* perms reftbl ... tbl mt/nil? */ - if(lua_istable(upi->L, -1)) { - /* perms reftbl ... tbl mt */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... tbl */ - } else { - /* perms reftbl ... tbl nil? */ - lua_assert(lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } - - while(1) - { - /* perms reftbl ... tbl */ - unpersist(upi); - /* perms reftbl ... tbl key/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - break; - } - /* perms reftbl ... tbl key */ - unpersist(upi); - /* perms reftbl ... tbl key value? */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl key value */ - lua_rawset(upi->L, -3); - /* perms reftbl ... tbl */ - } -} - -static void unpersisttable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - { - int isspecial; - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersistspecialtable(ref, upi); - /* perms reftbl ... tbl */ - } else { - unpersistliteraltable(ref, upi); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } -} - -static UpVal *makeupval(lua_State *L, int stackpos) -{ - UpVal *uv = pdep_new(L, UpVal); - pdep_link(L, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - setobj(L, uv->v, getobject(L, stackpos)); - return uv; -} - -static Proto *makefakeproto(lua_State *L, lu_byte nups) -{ - Proto *p = pdep_newproto(L); - p->sizelineinfo = 1; - p->lineinfo = pdep_newvector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = pdep_newvector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = pdep_newlstr(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -/* The GC is not fond of finding upvalues in tables. We get around this - * during persistence using a weakly keyed table, so that the GC doesn't - * bother to mark them. This won't work in unpersisting, however, since - * if we make the values weak they'll be collected (since nothing else - * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. */ -static void boxupval_start(lua_State *L) -{ - LClosure *lcl; - lcl = (LClosure *)pdep_newLclosure(L, 1, hvalue(&L->l_gt)); - pushclosure(L, (Closure *)lcl); - /* ... func */ - lcl->p = makefakeproto(L, 1); - - /* Temporarily initialize the upvalue to nil */ - - lua_pushnil(L); - lcl->upvals[0] = makeupval(L, -1); - lua_pop(L, 1); -} - -static void boxupval_finish(lua_State *L) -{ - /* ... func obj */ - LClosure *lcl = (LClosure *) clvalue(getobject(L, -2)); - - lcl->upvals[0]->u.value = *getobject(L, -1); - lua_pop(L, 1); -} - - -static void unboxupval(lua_State *L) -{ - /* ... func */ - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getobject(L, -1)); - uv = lcl->upvals[0]; - lua_pop(L, 1); - /* ... */ - pushupval(L, uv); - /* ... upval */ -} - -static void unpersistfunction(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - LClosure *lcl; - int i; - lu_byte nupvalues; - lua_checkstack(upi->L, 2); - - verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0); - - lcl = (LClosure *)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->l_gt)); - pushclosure(upi->L, (Closure *)lcl); - - /* perms reftbl ... func */ - /* Put *some* proto in the closure, before the GC can find it */ - lcl->p = makefakeproto(upi->L, nupvalues); - - /* Also, we need to temporarily fill the upvalues */ - lua_pushnil(upi->L); - /* perms reftbl ... func nil */ - for(i=0; i<nupvalues; i++) { - lcl->upvals[i] = makeupval(upi->L, -1); - } - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - /* I can't see offhand how a function would ever get to be self- - * referential, but just in case let's register it early */ - registerobject(ref, upi); - - /* Now that it's safe, we can get the real proto */ - unpersist(upi); - /* perms reftbl ... func proto? */ - lua_assert(lua_type(upi->L, -1) == LUA_TPROTO); - /* perms reftbl ... func proto */ - lcl->p = toproto(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - for(i=0; i<nupvalues; i++) { - /* perms reftbl ... func */ - unpersist(upi); - /* perms reftbl ... func func2 */ - unboxupval(upi->L); - /* perms reftbl ... func upval */ - lcl->upvals[i] = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - - /* Finally, the fenv */ - unpersist(upi); - /* perms reftbl ... func fenv/nil? */ - lua_assert(lua_type(upi->L, -1) == LUA_TNIL || - lua_type(upi->L, -1) == LUA_TTABLE); - /* perms reftbl ... func fenv/nil */ - if(!lua_isnil(upi->L, -1)) { - /* perms reftbl ... func fenv */ - lua_setfenv(upi->L, -2); - /* perms reftbl ... func */ - } else { - /* perms reftbl ... func nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ -} - -static void unpersistupval(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - - boxupval_start(upi->L); - /* perms reftbl ... func */ - registerobject(ref, upi); - - unpersist(upi); - /* perms reftbl ... func obj */ - boxupval_finish(upi->L); - /* perms reftbl ... func */ -} - -static void unpersistproto(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - Proto *p; - int i; - int sizep, sizek; - - /* We have to be careful. The GC expects a lot out of protos. In - * particular, we need to give the function a valid string for its - * source, and valid code, even before we actually read in the real - * code. */ - TString *source = pdep_newlstr(upi->L, "", 0); - p = pdep_newproto(upi->L); - p->source = source; - p->sizecode=1; - p->code = pdep_newvector(upi->L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->maxstacksize = 2; - p->sizek = 0; - p->sizep = 0; - - lua_checkstack(upi->L, 2); - - pushproto(upi->L, p); - /* perms reftbl ... proto */ - /* We don't need to register early, since protos can never ever be - * involved in cyclic references */ - - /* Read in constant references */ - { - verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue); - for(i=0; i<sizek; i++) { - /* perms reftbl ... proto */ - unpersist(upi); - /* perms reftbl ... proto k */ - setobj2s(upi->L, &p->k[i], getobject(upi->L, -1)); - p->sizek++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - /* Read in sub-proto references */ - { - verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*); - for(i=0; i<sizep; i++) { - /* perms reftbl ... proto */ - unpersist(upi); - /* perms reftbl ... proto subproto */ - p->p[i] = toproto(upi->L, -1); - p->sizep++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - - /* Read in code */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction); - verify(LIF(Z,read)(&upi->zio, p->code, - sizeof(Instruction) * p->sizecode) == 0); - } - - /* Read in upvalue names */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0); - if (p->sizeupvalues) - { - LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *); - for(i=0; i<p->sizeupvalues; i++) - { - unpersist(upi); - p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - } - } - } - - /* Read in local variable infos */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0); - if (p->sizelocvars) - { - LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar); - for(i=0; i<p->sizelocvars; i++) - { - unpersist(upi); - p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0); - } - } - } - - /* Read in source string*/ - unpersist(upi); - p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - /* Read in line numbers */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0); - if (p->sizelineinfo) - { - LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int); - verify(LIF(Z,read)(&upi->zio, p->lineinfo, - sizeof(int) * p->sizelineinfo) == 0); - } - } - - /* Read in linedefined and lastlinedefined */ - verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0); - - /* Read in misc values */ - { - verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0); - } -} - - -/* Does basically the opposite of luaC_link(). - * Right now this function is rather inefficient; it requires traversing the - * entire root GC set in order to find one object. If the GC list were doubly - * linked this would be much easier, but there's no reason for Lua to have - * that. */ -static void gcunlink(lua_State *L, GCObject *gco) -{ - GCObject *prevslot; - if(G(L)->rootgc == gco) { - G(L)->rootgc = G(L)->rootgc->gch.next; - return; - } - - prevslot = G(L)->rootgc; - while(prevslot->gch.next != gco) { - lua_assert(prevslot->gch.next != NULL); - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - -/* FIXME __ALL__ field ordering */ -static void unpersistthread(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_State *L2; - size_t stacklimit = 0; - L2 = lua_newthread(upi->L); - lua_checkstack(upi->L, 3); - /* L1: perms reftbl ... thr */ - /* L2: (empty) */ - registerobject(ref, upi); - - /* First, deserialize the object stack. */ - { - size_t i, stacksize; - read_size(&upi->zio, &stacksize); - LIF(D,growstack)(L2, (int)stacksize); - /* Make sure that the first stack element (a nil, representing - * the imaginary top-level C function) is written to the very, - * very bottom of the stack */ - L2->top--; - for(i=0; i<stacksize; i++) { - unpersist(upi); - /* L1: perms reftbl ... thr obj* */ - } - lua_xmove(upi->L, L2, stacksize); - /* L1: perms reftbl ... thr */ - /* L2: obj* */ - } - /* (hereafter, stacks refer to L1) */ - - /* Now, deserialize the CallInfo stack. */ - { - size_t i, numframes; - read_size(&upi->zio, &numframes); - LIF(D,reallocCI)(L2,numframes*2); - for(i=0; i<numframes; i++) { - CallInfo *ci = L2->base_ci + i; - size_t stackbase, stackfunc, stacktop, savedpc; - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stackfunc); - read_size(&upi->zio, &stacktop); - verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0); - read_size(&upi->zio, &savedpc); - - if(stacklimit < stacktop) - stacklimit = stacktop; - - ci->base = L2->stack+stackbase; - ci->func = L2->stack+stackfunc; - ci->top = L2->stack+stacktop; - ci->savedpc = (ci != L2->base_ci) ? - ci_func(ci)->l.p->code+savedpc : - 0; - ci->tailcalls = 0; - /* Update the pointer each time, to keep the GC - * happy*/ - L2->ci = ci; - } - } - /* perms reftbl ... thr */ - /* Deserialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase, stacktop; - L2->savedpc = L2->ci->savedpc; - verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0); - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stacktop); - -#ifdef SIZES64 - uint64 value; - verify(LIF(Z,read)(&upi->zio, &value, sizeof(uint64)) == 0); - - L2->errfunc = static_cast<ptrdiff_t>(value); -#else - verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0); -#endif - - //read_size(&upi->zio, (size_t *)&L2->errfunc); - L2->base = L2->stack + stackbase; - L2->top = L2->stack + stacktop; - } - /* Finally, "reopen" upvalues (see persistupval() for why) */ - { - UpVal* uv; - GCObject **nextslot = &L2->openupval; - global_State *g = G(L2); - while(1) { - size_t stackpos; - unpersist(upi); - /* perms reftbl ... thr uv/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... thr nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - break; - } - /* perms reftbl ... thr boxeduv */ - unboxupval(upi->L); - /* perms reftbl ... thr uv */ - uv = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - - read_size(&upi->zio, &stackpos); - uv->v = L2->stack + stackpos; - gcunlink(upi->L, (GCObject *)uv); - uv->marked = luaC_white(g); - *nextslot = (GCObject *)uv; - nextslot = &uv->next; - uv->u.l.prev = &G(L2)->uvhead; - uv->u.l.next = G(L2)->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - G(L2)->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - } - *nextslot = NULL; - } - - /* The stack must be valid at least to the highest value among the CallInfos */ - /* 'top' and the values up to there must be filled with 'nil' */ - { - StkId o; - LIF(D,checkstack)(L2, (int)stacklimit); - for (o = L2->top; o <= L2->top + stacklimit; o++) - setnilvalue(o); - } -} - -static void unpersistuserdata(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int isspecial; - lua_checkstack(upi->L, 2); - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(upi->L, &upi->zio); - lua_call(upi->L, 1, 1); -#else - lua_call(upi->L, 0, 1); -#endif - /* perms reftbl ... udata? */ -/* This assertion might not be necessary; it's conceivable, for - * example, that the SP function might decide to return a table - * with equivalent functionality. For the time being, we'll - * ignore this possibility in favor of stricter and more testable - * requirements. */ - lua_assert(lua_isuserdata(upi->L, -1)); - /* perms reftbl ... udata */ - } else { - size_t length; - read_size(&upi->zio, &length); - - lua_newuserdata(upi->L, length); - /* perms reftbl ... udata */ - registerobject(ref, upi); - verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0); - - unpersist(upi); - /* perms reftbl ... udata mt/nil? */ - lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1)); - /* perms reftbl ... udata mt/nil */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... udata */ - } - /* perms reftbl ... udata */ -} - -static void unpersistpermanent(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - unpersist(upi); - /* perms reftbl permkey */ - lua_gettable(upi->L, 1); - /* perms reftbl perm? */ - /* We assume currently that the substituted permanent value - * shouldn't be nil. This may be a bad assumption. Real-life - * experience is needed to evaluate this. */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl perm */ -} - -#if 0 -/* For debugging only; not called when lua_assert is empty */ -static int inreftable(lua_State *L, int ref) -{ - int res; - lua_checkstack(L, 1); - /* perms reftbl ... */ - lua_pushlightuserdata(L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(L, 2); - /* perms reftbl ... obj? */ - res = !lua_isnil(L, -1); - lua_pop(L, 1); - /* perms reftbl ... */ - return res; -} -#endif - -static void unpersist(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int firstTime; - int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */ - lua_checkstack(upi->L, 2); - LIF(Z,read)(&upi->zio, &firstTime, sizeof(int)); - if(firstTime) { - int ref; - int type; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); - lua_assert(!inreftable(upi->L, ref)); - LIF(Z,read)(&upi->zio, &type, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("1 %d %d\n", ref, type); - upi->level++; -#endif - switch(type) { - case LUA_TBOOLEAN: - unpersistboolean(upi); - break; - case LUA_TLIGHTUSERDATA: - unpersistlightuserdata(upi); - break; - case LUA_TNUMBER: - unpersistnumber(upi); - break; - case LUA_TSTRING: - unpersiststring(upi); - break; - case LUA_TTABLE: - unpersisttable(ref, upi); - break; - case LUA_TFUNCTION: - unpersistfunction(ref, upi); - break; - case LUA_TTHREAD: - unpersistthread(ref, upi); - break; - case LUA_TPROTO: - unpersistproto(ref, upi); - break; - case LUA_TUPVAL: - unpersistupval(ref, upi); - break; - case LUA_TUSERDATA: - unpersistuserdata(ref, upi); - break; - case PLUTO_TPERMANENT: - unpersistpermanent(ref, upi); - break; - default: - lua_assert(0); - } - /* perms reftbl ... obj */ - lua_assert(lua_type(upi->L, -1) == type || - type == PLUTO_TPERMANENT || - /* Remember, upvalues get a special dispensation, as - * described in boxupval */ - (lua_type(upi->L, -1) == LUA_TFUNCTION && - type == LUA_TUPVAL)); - registerobject(ref, upi); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - upi->level--; -#endif - } else { - int ref; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("0 %d\n", ref); -#endif - if(ref == 0) { - lua_pushnil(upi->L); - /* perms reftbl ... nil */ - } else { - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(upi->L, 2); - /* perms reftbl ... obj? */ - lua_assert(!lua_isnil(upi->L, -1)); - } - /* perms reftbl ... obj/nil */ - } - /* perms reftbl ... obj/nil */ - lua_assert(lua_gettop(upi->L) == stacksize + 1); -} - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) -{ - /* We use the graciously provided ZIO (what the heck does the Z stand - * for?) library so that we don't have to deal with the reader directly. - * Letting the reader function decide how much data to return can be - * very unpleasant. - */ - UnpersistInfo upi; - upi.L = L; -#ifdef PLUTO_DEBUG - upi.level = 0; -#endif - - lua_checkstack(L, 3); - LIF(Z,init)(L, &upi.zio, reader, ud); - - /* perms */ - lua_newtable(L); - /* perms reftbl */ - lua_gc(L, LUA_GCSTOP, 0); - unpersist(&upi); - lua_gc(L, LUA_GCRESTART, 0); - /* perms reftbl rootobj */ - lua_replace(L, 2); - /* perms rootobj */ -} - -typedef struct LoadInfo_t { - char *buf; - size_t size; -} LoadInfo; - - -static const char *bufreader(lua_State *L, void *ud, size_t *sz) { - LoadInfo *li = (LoadInfo *)ud; - if(li->size == 0) { - return NULL; - } - *sz = li->size; - li->size = 0; - return li->buf; -} - -int unpersist_l(lua_State *L) -{ - LoadInfo li; - char const *origbuf; - char *tempbuf; - size_t bufsize; - /* perms? str? ...? */ - lua_settop(L, 2); - /* perms? str? */ - origbuf = luaL_checklstring(L, 2, &bufsize); - tempbuf = LIF(M,newvector)(L, bufsize, char); - memcpy(tempbuf, origbuf, bufsize); - - li.buf = tempbuf; - li.size = bufsize; - - /* perms? str */ - lua_pop(L, 1); - /* perms? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms */ - pluto_unpersist(L, bufreader, &li); - /* perms rootobj */ - LIF(M,freearray)(L, tempbuf, bufsize, char); - return 1; -} - -/* Stefan's first C function for Lua! :) - Returns a string describing the Pluto version you're using. */ - -int version_l(lua_State *L) -{ - const char *version = VERSION; - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, version, strlen(version)); - /* str */ - return 1; -} - -/* Set human-readable output on or off. */ -int human_l(lua_State *L) -{ - /* flag? ...? */ - lua_settop(L, 1); - /* flag? */ - /*luaL_checktype(L, 1, LUA_TBOOLEAN);*/ - /* flag */ - - humanReadable = lua_toboolean(L, 1); - - lua_settop(L, 0); - /* (empty) */ - return 0; -} - -static luaL_reg pluto_reg[] = { - { "persist", persist_l }, - { "unpersist", unpersist_l }, - { "version", version_l }, - { "human", human_l }, - { NULL, NULL } -}; - -LUALIB_API int luaopen_pluto(lua_State *L) { - luaL_openlib(L, "pluto", pluto_reg, 0); - return 1; -} diff --git a/engines/sword25/util/pluto/pluto.h b/engines/sword25/util/pluto/pluto.h deleted file mode 100644 index 3674842d44..0000000000 --- a/engines/sword25/util/pluto/pluto.h +++ /dev/null @@ -1,25 +0,0 @@ -/* $Id$ */ - -/* Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* lua.h must be included before this file */ - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud); - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud); - -LUALIB_API int luaopen_pluto(lua_State *L); diff --git a/engines/sword25/util/pluto/plzio.cpp b/engines/sword25/util/pluto/plzio.cpp deleted file mode 100644 index 21f69a9e8d..0000000000 --- a/engines/sword25/util/pluto/plzio.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -** $Id$ -** a generic input stream interface -** See Copyright Notice in lua.h -*/ - - -#include <string.h> - -#define lzio_c -#define LUA_CORE - -#include "pdep/pdep.h" - -int pdep_fill (ZIO *z) { - size_t size; - lua_State *L = z->L; - const char *buff; - lua_unlock(L); - buff = z->reader(L, z->data, &size); - lua_lock(L); - if (buff == NULL || size == 0) return EOZ; - z->n = size - 1; - z->p = buff; - return char2int(*(z->p++)); -} - - -int pdep_lookahead (ZIO *z) { - if (z->n == 0) { - if (pdep_fill(z) == EOZ) - return EOZ; - else { - z->n++; /* pdep_fill removed first byte; put back it */ - z->p--; - } - } - return char2int(*z->p); -} - - -void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { - z->L = L; - z->reader = reader; - z->data = data; - z->n = 0; - z->p = NULL; -} - - -/* --------------------------------------------------------------- read --- */ -size_t pdep_read (ZIO *z, void *b, size_t n) { - while (n) { - size_t m; - if (pdep_lookahead(z) == EOZ) - return n; /* return number of missing bytes */ - m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ - memcpy(b, z->p, m); - z->n -= m; - z->p += m; - b = (char *)b + m; - n -= m; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ -char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - pdep_resizebuffer(L, buff, n); - } - return buff->buffer; -} diff --git a/engines/zvision/configure.engine b/engines/zvision/configure.engine index 02e31943af..38a5959995 100644 --- a/engines/zvision/configure.engine +++ b/engines/zvision/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine zvision "ZVision" no "" "" "freetype2 16bit" +add_engine zvision "ZVision" yes "" "" "freetype2 16bit" diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp index b5e542d777..f39c06b57e 100644 --- a/engines/zvision/core/console.cpp +++ b/engines/zvision/core/console.cpp @@ -53,6 +53,7 @@ Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) { registerCmd("location", WRAP_METHOD(Console, cmdLocation)); registerCmd("dumpfile", WRAP_METHOD(Console, cmdDumpFile)); registerCmd("dumpfiles", WRAP_METHOD(Console, cmdDumpFiles)); + registerCmd("dumpimage", WRAP_METHOD(Console, cmdDumpImage)); } bool Console::cmdLoadVideo(int argc, const char **argv) { @@ -262,11 +263,70 @@ bool Console::cmdDumpFiles(int argc, const char **argv) { debugPrintf("Dumping %s\n", fileName.c_str()); in = iter->_value.arch->createReadStreamForMember(iter->_value.name); - dumpFile(in, fileName.c_str()); + if (in) + dumpFile(in, fileName.c_str()); delete in; } return true; } +bool Console::cmdDumpImage(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Use %s <TGA/TGZ name> to dump a ZVision TGA/TGZ image into a regular BMP image\n", argv[0]); + return true; + } + + Common::String fileName = argv[1]; + if (!fileName.hasSuffix(".tga")) { + debugPrintf("%s is not an image file", argv[1]); + } + + Common::File f; + if (!_engine->getSearchManager()->openFile(f, argv[1])) { + warning("File not found: %s", argv[1]); + return true; + } + + Graphics::Surface surface; + _engine->getRenderManager()->readImageToSurface(argv[1], surface, false); + + // Open file + Common::DumpFile out; + + fileName.setChar('b', fileName.size() - 3); + fileName.setChar('m', fileName.size() - 2); + fileName.setChar('p', fileName.size() - 1); + + out.open(fileName); + + // Write BMP header + out.writeByte('B'); + out.writeByte('M'); + out.writeUint32LE(surface.h * surface.pitch + 54); + out.writeUint32LE(0); + out.writeUint32LE(54); + out.writeUint32LE(40); + out.writeUint32LE(surface.w); + out.writeUint32LE(surface.h); + out.writeUint16LE(1); + out.writeUint16LE(16); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + + // Write pixel data to BMP + out.write(surface.getPixels(), surface.pitch * surface.h); + + out.flush(); + out.close(); + + surface.free(); + + return true; +} + } // End of namespace ZVision diff --git a/engines/zvision/core/console.h b/engines/zvision/core/console.h index a7bd88ebc3..ffce87869f 100644 --- a/engines/zvision/core/console.h +++ b/engines/zvision/core/console.h @@ -47,6 +47,7 @@ private: bool cmdLocation(int argc, const char **argv); bool cmdDumpFile(int argc, const char **argv); bool cmdDumpFiles(int argc, const char **argv); + bool cmdDumpImage(int argc, const char **argv); }; } // End of namespace ZVision diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp index 1eaff83413..545ebe35d4 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -59,6 +59,7 @@ namespace ZVision { #define GAMEOPTION_DOUBLE_FPS GUIO_GAMEOPTIONS2 #define GAMEOPTION_ENABLE_VENUS GUIO_GAMEOPTIONS3 #define GAMEOPTION_DISABLE_ANIM_WHILE_TURNING GUIO_GAMEOPTIONS4 +#define GAMEOPTION_USE_HIRES_MPEG_MOVIES GUIO_GAMEOPTIONS5 static const ZVisionGameDescription gameDescriptions[] = { @@ -77,6 +78,40 @@ static const ZVisionGameDescription gameDescriptions[] = { }, { + // Zork Nemesis French version + { + "znemesis", + 0, + {{"CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873}, + {"NEMESIS.STR", 0, "333bcb17bbb7f57cae742fbbe44f56f3", 9219}, + AD_LISTEND + }, + Common::FR_FRA, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { + // Zork Nemesis German version + { + "znemesis", + 0, + {{"CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873}, + {"NEMESIS.STR", 0, "3d1a12b907751653866cffc6d4dfb331", 9505}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { // Zork Nemesis English demo version { "znemesis", @@ -113,7 +148,7 @@ static const ZVisionGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_USE_HIRES_MPEG_MOVIES) }, GID_GRANDINQUISITOR }, @@ -160,7 +195,7 @@ static const ADExtraGuiOptionsMap optionsList[] = { GAMEOPTION_DOUBLE_FPS, { _s("Double FPS"), - _s("Halve the update delay"), + _s("Increase game FPS from 30 to 60"), "doublefps", false } @@ -186,6 +221,16 @@ static const ADExtraGuiOptionsMap optionsList[] = { } }, + { + GAMEOPTION_USE_HIRES_MPEG_MOVIES, + { + _s("Use the hires MPEG movies"), + _s("Use the hires MPEG movies of the DVD version, instead of the lowres AVI ones"), + "mpegmovies", + true + } + }, + AD_EXTRA_GUI_OPTIONS_TERMINATOR }; @@ -236,7 +281,7 @@ Common::Error ZVision::ZVision::loadGameState(int slot) { } Common::Error ZVision::ZVision::saveGameState(int slot, const Common::String &desc) { - _saveManager->saveGame(slot, desc); + _saveManager->saveGame(slot, desc, false); return Common::kNoError; } diff --git a/engines/zvision/file/save_manager.cpp b/engines/zvision/file/save_manager.cpp index 042fafd38e..5e96e4ab5e 100644 --- a/engines/zvision/file/save_manager.cpp +++ b/engines/zvision/file/save_manager.cpp @@ -69,7 +69,7 @@ bool SaveManager::scummVMSaveLoadDialog(bool isSave) { return false; if (isSave) { - saveGame(slot, desc); + saveGame(slot, desc, false); return true; } else { Common::ErrorCode result = loadGame(slot).getCode(); @@ -77,46 +77,34 @@ bool SaveManager::scummVMSaveLoadDialog(bool isSave) { } } -void SaveManager::saveGame(uint slot, const Common::String &saveName) { - Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); - Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - - writeSaveGameHeader(file, saveName); +void SaveManager::saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer) { + if (!_tempSave && useSaveBuffer) + return; - _engine->getScriptManager()->serialize(file); - - file->finalize(); - delete file; - - _lastSaveTime = g_system->getMillis(); -} - -void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream) { Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - writeSaveGameHeader(file, saveName); + writeSaveGameHeader(file, saveName, useSaveBuffer); - file->write(stream->getData(), stream->size()); + if (useSaveBuffer) + file->write(_tempSave->getData(), _tempSave->size()); + else + _engine->getScriptManager()->serialize(file); file->finalize(); delete file; - _lastSaveTime = g_system->getMillis(); -} - -void SaveManager::saveGameBuffered(uint slot, const Common::String &saveName) { - if (_tempSave) { - saveGame(slot, saveName, _tempSave); + if (useSaveBuffer) flushSaveBuffer(); - } + + _lastSaveTime = g_system->getMillis(); } void SaveManager::autoSave() { - saveGame(0, "Auto save"); + saveGame(0, "Auto save", false); } -void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName) { +void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer) { file->writeUint32BE(SAVEGAME_ID); // Write version @@ -126,8 +114,11 @@ void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::S file->writeString(saveName); file->writeByte(0); - // Create a thumbnail and save it - Graphics::saveThumbnail(*file); + // Save the game thumbnail + if (useSaveBuffer) + file->write(_tempThumbnail->getData(), _tempThumbnail->size()); + else + Graphics::saveThumbnail(*file); // Write out the save date/time TimeDate td; @@ -139,39 +130,25 @@ void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::S file->writeSint16LE(td.tm_min); } -Common::Error SaveManager::loadGame(uint slot) { - Common::SeekableReadStream *saveFile = getSlotFile(slot); - if (saveFile == 0) { - return Common::kPathDoesNotExist; - } - - // Read the header - SaveGameHeader header; - if (!readSaveGameHeader(saveFile, header)) { - return Common::kUnknownError; - } - - ScriptManager *scriptManager = _engine->getScriptManager(); - // Update the state table values - scriptManager->deserialize(saveFile); - - delete saveFile; - if (header.thumbnail) - delete header.thumbnail; - - return Common::kNoError; -} - -Common::Error SaveManager::loadGame(const Common::String &saveName) { - Common::File *saveFile = _engine->getSearchManager()->openFile(saveName); - if (saveFile == NULL) { - saveFile = new Common::File; - if (!saveFile->open(saveName)) { - delete saveFile; - return Common::kPathDoesNotExist; +Common::Error SaveManager::loadGame(int slot) { + Common::SeekableReadStream *saveFile = NULL; + + if (slot >= 0) { + saveFile = getSlotFile(slot); + } else { + Common::File *saveFile = _engine->getSearchManager()->openFile("r.svr"); + if (!saveFile) { + saveFile = new Common::File; + if (!saveFile->open("r.svr")) { + delete saveFile; + return Common::kPathDoesNotExist; + } } } + if (!saveFile) + return Common::kPathDoesNotExist; + // Read the header SaveGameHeader header; if (!readSaveGameHeader(saveFile, header)) { @@ -272,18 +249,20 @@ Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) { } void SaveManager::prepareSaveBuffer() { - if (_tempSave) - delete _tempSave; + delete _tempThumbnail; + _tempThumbnail = new Common::MemoryWriteStreamDynamic; + Graphics::saveThumbnail(*_tempThumbnail); + delete _tempSave; _tempSave = new Common::MemoryWriteStreamDynamic; - _engine->getScriptManager()->serialize(_tempSave); } void SaveManager::flushSaveBuffer() { - if (_tempSave) - delete _tempSave; + delete _tempThumbnail; + _tempThumbnail = NULL; + delete _tempSave; _tempSave = NULL; } diff --git a/engines/zvision/file/save_manager.h b/engines/zvision/file/save_manager.h index fc8db67566..9e816373ea 100644 --- a/engines/zvision/file/save_manager.h +++ b/engines/zvision/file/save_manager.h @@ -48,7 +48,7 @@ struct SaveGameHeader { class SaveManager { public: - SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _lastSaveTime(0) {} + SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _tempThumbnail(NULL), _lastSaveTime(0) {} ~SaveManager() { flushSaveBuffer(); } @@ -67,6 +67,7 @@ private: SAVE_VERSION = 1 }; + Common::MemoryWriteStreamDynamic *_tempThumbnail; Common::MemoryWriteStreamDynamic *_tempSave; public: @@ -83,17 +84,14 @@ public: * @param slot The save slot this save pertains to. Must be [1, 20] * @param saveName The internal name for this save. This is NOT the name of the actual save file. */ - void saveGame(uint slot, const Common::String &saveName); - void saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream); - void saveGameBuffered(uint slot, const Common::String &saveName); + void saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer); /** * Loads the state data from the save file that slot references. Uses * ZVision::generateSaveFileName(slot) to get the save file name. * * @param slot The save slot to load. Must be [1, 20] */ - Common::Error loadGame(uint slot); - Common::Error loadGame(const Common::String &saveName); + Common::Error loadGame(int slot); Common::SeekableReadStream *getSlotFile(uint slot); bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header); @@ -102,7 +100,7 @@ public: void flushSaveBuffer(); bool scummVMSaveLoadDialog(bool isSave); private: - void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName); + void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer); }; } // End of namespace ZVision diff --git a/engines/zvision/file/search_manager.cpp b/engines/zvision/file/search_manager.cpp index ec250ff648..9f709dd0a1 100644 --- a/engines/zvision/file/search_manager.cpp +++ b/engines/zvision/file/search_manager.cpp @@ -62,19 +62,6 @@ SearchManager::~SearchManager() { _archList.clear(); } -void SearchManager::addPatch(const Common::String &src, const Common::String &dst) { - Common::String lowerCaseName = dst; - lowerCaseName.toLowercase(); - - SearchManager::MatchList::iterator it = _files.find(lowerCaseName); - - if (it != _files.end()) { - lowerCaseName = src; - lowerCaseName.toLowercase(); - _files[lowerCaseName] = it->_value; - } -} - void SearchManager::addFile(const Common::String &name, Common::Archive *arch) { bool addArch = true; Common::List<Common::Archive *>::iterator it = _archList.begin(); @@ -147,9 +134,10 @@ bool SearchManager::hasFile(const Common::String &name) { return false; } -void SearchManager::loadZix(const Common::String &name) { +bool SearchManager::loadZix(const Common::String &name) { Common::File file; - file.open(name); + if (!file.open(name)) + return false; Common::String line; @@ -160,7 +148,7 @@ void SearchManager::loadZix(const Common::String &name) { } if (file.eos()) - return; + error("Corrupt ZIX file: %s", name.c_str()); Common::Array<Common::Archive *> archives; @@ -169,42 +157,37 @@ void SearchManager::loadZix(const Common::String &name) { line.trim(); if (line.matchString("----------*", true)) break; - else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true)) { + else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true) || line.matchString("CD2:*", true)) { + Common::Archive *arc; + Common::String path(line.c_str() + 5); + for (uint i = 0; i < path.size(); i++) + if (path[i] == '\\') + path.setChar('/', i); + + // Check if NEMESIS.ZIX/MEDIUM.ZIX refers to the znemesis folder, and + // check the game root folder instead + if (path.hasPrefix("znemesis/")) + path = Common::String(path.c_str() + 9); + // Check if INQUIS.ZIX refers to the ZGI folder, and check the game // root folder instead - if (path.hasPrefix("zgi\\")) + if (path.hasPrefix("zgi/")) path = Common::String(path.c_str() + 4); + if (path.hasPrefix("zgi_e/")) + path = Common::String(path.c_str() + 6); - Common::Archive *arc; - char tempPath[128]; - strcpy(tempPath, path.c_str()); - for (uint i = 0; i < path.size(); i++) - if (tempPath[i] == '\\') - tempPath[i] = '/'; - - path = Common::String(tempPath); if (path.size() && path[0] == '.') path.deleteChar(0); if (path.size() && path[0] == '/') path.deleteChar(0); + if (path.size() && path.hasSuffix("/")) + path.deleteLastChar(); - if (path.matchString("*.zfs", true)) + if (path.matchString("*.zfs", true)) { arc = new ZfsArchive(path); - else { - if (path.size()) { - if (path[path.size() - 1] == '\\' || path[path.size() - 1] == '/') - path.deleteLastChar(); - if (path.size()) - for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it) - if (path.equalsIgnoreCase(*it)) { - path = *it; - break; - } - } - + } else { path = Common::String::format("%s/%s", _root.c_str(), path.c_str()); - arc = new Common::FSDirectory(path); } archives.push_back(arc); @@ -212,7 +195,7 @@ void SearchManager::loadZix(const Common::String &name) { } if (file.eos()) - return; + error("Corrupt ZIX file: %s", name.c_str()); while (!file.eos()) { line = file.readLine(); @@ -225,6 +208,8 @@ void SearchManager::loadZix(const Common::String &name) { } } } + + return true; } void SearchManager::addDir(const Common::String &name) { diff --git a/engines/zvision/file/search_manager.h b/engines/zvision/file/search_manager.h index b9ed02ec13..0d0ab14d31 100644 --- a/engines/zvision/file/search_manager.h +++ b/engines/zvision/file/search_manager.h @@ -39,13 +39,12 @@ public: void addFile(const Common::String &name, Common::Archive *arch); void addDir(const Common::String &name); - void addPatch(const Common::String &src, const Common::String &dst); Common::File *openFile(const Common::String &name); bool openFile(Common::File &file, const Common::String &name); bool hasFile(const Common::String &name); - void loadZix(const Common::String &name); + bool loadZix(const Common::String &name); struct Node { Common::String name; diff --git a/engines/zvision/graphics/cursors/cursor.cpp b/engines/zvision/graphics/cursors/cursor.cpp index f32c68645d..2c011668ac 100644 --- a/engines/zvision/graphics/cursors/cursor.cpp +++ b/engines/zvision/graphics/cursors/cursor.cpp @@ -43,7 +43,7 @@ ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) _hotspotY(0) { Common::File file; if (!engine->getSearchManager()->openFile(file, fileName)) - return; + error("Cursor file %s does not exist", fileName.c_str()); uint32 magic = file.readUint32BE(); if (magic != MKTAG('Z', 'C', 'R', '1')) { diff --git a/engines/zvision/graphics/cursors/cursor_manager.cpp b/engines/zvision/graphics/cursors/cursor_manager.cpp index c364426bad..1e048efedf 100644 --- a/engines/zvision/graphics/cursors/cursor_manager.cpp +++ b/engines/zvision/graphics/cursors/cursor_manager.cpp @@ -37,7 +37,7 @@ const char *CursorManager::_cursorNames[NUM_CURSORS] = { "active", "arrow", "bac "hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow" }; -const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac001.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", +const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac011.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", "g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr" }; @@ -55,6 +55,11 @@ CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat pixelF for (int i = 0; i < NUM_CURSORS; i++) { if (_engine->getGameId() == GID_NEMESIS) { Common::String name; + if (i == 1) { + // Cursors "arrowa.zcr" and "arrowb.zcr" are missing + _cursors[i][0] = _cursors[i][1] = ZorkCursor(); + continue; + } name = Common::String::format("%sa.zcr", _zNemCursorFileNames[i]); _cursors[i][0] = ZorkCursor(_engine, name); // Up cursor name = Common::String::format("%sb.zcr", _zNemCursorFileNames[i]); diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp index a1cc8ac53c..4a43e09b07 100644 --- a/engines/zvision/graphics/render_manager.cpp +++ b/engines/zvision/graphics/render_manager.cpp @@ -42,28 +42,25 @@ namespace ZVision { RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat, bool doubleFPS) : _engine(engine), _system(engine->_system), - _workingWidth(workingWindow.width()), - _workingHeight(workingWindow.height()), - _screenCenterX(_workingWidth / 2), - _screenCenterY(_workingHeight / 2), + _screenCenterX(_workingWindow.width() / 2), + _screenCenterY(_workingWindow.height() / 2), _workingWindow(workingWindow), _pixelFormat(pixelFormat), _backgroundWidth(0), _backgroundHeight(0), _backgroundOffset(0), - _renderTable(_workingWidth, _workingHeight), - _doubleFPS(doubleFPS) { + _renderTable(_workingWindow.width(), _workingWindow.height()), + _doubleFPS(doubleFPS), + _subid(0) { - _backgroundSurface.create(_workingWidth, _workingHeight, _pixelFormat); - _effectSurface.create(_workingWidth, _workingHeight, _pixelFormat); - _warpedSceneSurface.create(_workingWidth, _workingHeight, _pixelFormat); + _backgroundSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat); + _effectSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat); + _warpedSceneSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat); _menuSurface.create(windowWidth, workingWindow.top, _pixelFormat); - _subtitleSurface.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat); - + _menuArea = Common::Rect(0, 0, windowWidth, workingWindow.top); - _subtitleArea = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight); - _subid = 0; + initSubArea(windowWidth, windowHeight, workingWindow); } RenderManager::~RenderManager() { @@ -83,7 +80,7 @@ void RenderManager::renderSceneToScreen() { // If we have graphical effects, we apply them using a temporary buffer if (!_effects.empty()) { bool copied = false; - Common::Rect windowRect(_workingWidth, _workingHeight); + Common::Rect windowRect(_workingWindow.width(), _workingWindow.height()); for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { Common::Rect rect = (*it)->getRegion(); @@ -121,7 +118,7 @@ void RenderManager::renderSceneToScreen() { if (!_backgroundSurfaceDirtyRect.isEmpty()) { _renderTable.mutateImage(&_warpedSceneSurface, in); out = &_warpedSceneSurface; - outWndDirtyRect = Common::Rect(_workingWidth, _workingHeight); + outWndDirtyRect = Common::Rect(_workingWindow.width(), _workingWindow.height()); } } else { out = in; @@ -590,7 +587,7 @@ void RenderManager::prepareBackground() { if (state == RenderTable::PANORAMA) { // Calculate the visible portion of the background - Common::Rect viewPort(_workingWidth, _workingHeight); + Common::Rect viewPort(_workingWindow.width(), _workingWindow.height()); viewPort.translate(-(_screenCenterX - _backgroundOffset), 0); Common::Rect drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); @@ -635,7 +632,7 @@ void RenderManager::prepareBackground() { } } else if (state == RenderTable::TILT) { // Tilt doesn't allow wrapping, so we just do a simple clip - Common::Rect viewPort(_workingWidth, _workingHeight); + Common::Rect viewPort(_workingWindow.width(), _workingWindow.height()); viewPort.translate(0, -(_screenCenterY - _backgroundOffset)); Common::Rect drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); @@ -655,7 +652,7 @@ void RenderManager::prepareBackground() { // Clear the dirty rect since everything is clean now _backgroundDirtyRect = Common::Rect(); - _backgroundSurfaceDirtyRect.clip(_workingWidth, _workingHeight); + _backgroundSurfaceDirtyRect.clip(_workingWindow.width(), _workingWindow.height()); } void RenderManager::clearMenuSurface() { @@ -687,6 +684,15 @@ void RenderManager::renderMenuToScreen() { } } +void RenderManager::initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow) { + _workingWindow = workingWindow; + + _subtitleSurface.free(); + + _subtitleSurface.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat); + _subtitleArea = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight); +} + uint16 RenderManager::createSubArea(const Common::Rect &area) { _subid++; @@ -791,8 +797,8 @@ Common::Rect RenderManager::transformBackgroundSpaceRectToScreenSpace(const Comm if (state == RenderTable::PANORAMA) { if (_backgroundOffset < _screenCenterX) { - Common::Rect rScreen(_screenCenterX + _backgroundOffset, _workingHeight); - Common::Rect lScreen(_workingWidth - rScreen.width(), _workingHeight); + Common::Rect rScreen(_screenCenterX + _backgroundOffset, _workingWindow.height()); + Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height()); lScreen.translate(_backgroundWidth - lScreen.width(), 0); lScreen.clip(src); rScreen.clip(src); @@ -802,8 +808,8 @@ Common::Rect RenderManager::transformBackgroundSpaceRectToScreenSpace(const Comm tmp.translate(_screenCenterX - _backgroundOffset, 0); } } else if (_backgroundWidth - _backgroundOffset < _screenCenterX) { - Common::Rect rScreen(_screenCenterX - (_backgroundWidth - _backgroundOffset), _workingHeight); - Common::Rect lScreen(_workingWidth - rScreen.width(), _workingHeight); + Common::Rect rScreen(_screenCenterX - (_backgroundWidth - _backgroundOffset), _workingWindow.height()); + Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height()); lScreen.translate(_backgroundWidth - lScreen.width(), 0); lScreen.clip(src); rScreen.clip(src); @@ -1172,4 +1178,11 @@ void RenderManager::rotateTo(int16 _toPos, int16 _time) { _engine->startClock(); } +void RenderManager::upscaleRect(Common::Rect &rect) { + rect.top = rect.top * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT; + rect.left = rect.left * HIRES_WINDOW_WIDTH / WINDOW_WIDTH; + rect.bottom = rect.bottom * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT; + rect.right = rect.right * HIRES_WINDOW_WIDTH / WINDOW_WIDTH; +} + } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h index c22f9a78c9..e3cbbc34ce 100644 --- a/engines/zvision/graphics/render_manager.h +++ b/engines/zvision/graphics/render_manager.h @@ -73,12 +73,8 @@ private: * are given in this coordinate space. Also, all images are clipped to the * edges of this Rectangle */ - const Common::Rect _workingWindow; + Common::Rect _workingWindow; - // Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() - const int _workingWidth; - // Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() - const int _workingHeight; // Center of the screen in the x direction const int _screenCenterX; // Center of the screen in the y direction @@ -106,7 +102,6 @@ private: // A buffer for subtitles Graphics::Surface _subtitleSurface; - Common::Rect _subtitleSurfaceDirtyRect; // Rectangle for subtitles area Common::Rect _subtitleArea; @@ -242,6 +237,8 @@ public: // Subtitles methods + void initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow); + // Create subtitle area and return ID uint16 createSubArea(const Common::Rect &area); uint16 createSubArea(); @@ -335,6 +332,8 @@ public: void checkBorders(); void rotateTo(int16 to, int16 time); void updateRotation(); + + void upscaleRect(Common::Rect &rect); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index a61fa26223..e989478dd1 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -21,14 +21,13 @@ */ #include "common/scummsys.h" +#include "video/video_decoder.h" #include "zvision/scripting/actions.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" -#include "zvision/sound/zork_raw.h" -#include "zvision/video/zork_avi_decoder.h" #include "zvision/file/save_manager.h" #include "zvision/scripting/menu.h" #include "zvision/scripting/effects/timer_effect.h" @@ -46,10 +45,6 @@ #include "zvision/graphics/effects/wave.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "common/file.h" - -#include "audio/decoders/wave.h" - namespace ZVision { ////////////////////////////////////////////////////////////////////////////// @@ -815,7 +810,7 @@ ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotkey, const Commo } bool ActionRestoreGame::execute() { - _engine->getSaveManager()->loadGame(_fileName); + _engine->getSaveManager()->loadGame(-1); return false; } @@ -932,35 +927,62 @@ ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Commo } bool ActionStreamVideo::execute() { - ZorkAVIDecoder decoder; - Common::File *_file = _engine->getSearchManager()->openFile(_fileName); - - if (_file) { - if (!decoder.loadStream(_file)) { + Video::VideoDecoder *decoder; + Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + Common::String subname = _fileName; + subname.setChar('s', subname.size() - 3); + subname.setChar('u', subname.size() - 2); + subname.setChar('b', subname.size() - 1); + bool subtitleExists = _engine->getSearchManager()->hasFile(subname); + bool switchToHires = false; + +// NOTE: We only show the hires MPEG2 videos when libmpeg2 is compiled in, +// otherwise we fall back to the lowres ones +#ifdef USE_MPEG2 + Common::String hiresFileName = _fileName; + hiresFileName.setChar('d', hiresFileName.size() - 8); + hiresFileName.setChar('v', hiresFileName.size() - 3); + hiresFileName.setChar('o', hiresFileName.size() - 2); + hiresFileName.setChar('b', hiresFileName.size() - 1); + + if (_engine->getScriptManager()->getStateValue(StateKey_MPEGMovies) == 1 &&_engine->getSearchManager()->hasFile(hiresFileName)) { + // TODO: Enable once AC3 support is implemented + if (!_engine->getSearchManager()->hasFile(_fileName)) // Check for the regular video return true; - } - - _engine->getCursorManager()->showMouse(false); + warning("The hires videos of the DVD version of ZGI aren't supported yet, using lowres"); + //_fileName = hiresFileName; + //switchToHires = true; + } else if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; +#else + if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; +#endif - Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + decoder = _engine->loadAnimation(_fileName); + Subtitle *sub = (subtitleExists) ? new Subtitle(_engine, subname, switchToHires) : NULL; - Common::String subname = _fileName; - subname.setChar('s', subname.size() - 3); - subname.setChar('u', subname.size() - 2); - subname.setChar('b', subname.size() - 1); + _engine->getCursorManager()->showMouse(false); - Subtitle *sub = NULL; + if (switchToHires) { + _engine->initHiresScreen(); + destRect = Common::Rect(40, -40, 760, 440); + Common::Rect workingWindow = _engine->_workingWindow; + workingWindow.translate(0, -40); + _engine->getRenderManager()->initSubArea(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, workingWindow); + } - if (_engine->getSearchManager()->hasFile(subname)) - sub = new Subtitle(_engine, subname); + _engine->playVideo(*decoder, destRect, _skippable, sub); - _engine->playVideo(decoder, destRect, _skippable, sub); + if (switchToHires) { + _engine->initScreen(); + _engine->getRenderManager()->initSubArea(WINDOW_WIDTH, WINDOW_HEIGHT, _engine->_workingWindow); + } - _engine->getCursorManager()->showMouse(true); + _engine->getCursorManager()->showMouse(true); - if (sub) - delete sub; - } + delete decoder; + delete sub; return true; } diff --git a/engines/zvision/scripting/controls/save_control.cpp b/engines/zvision/scripting/controls/save_control.cpp index 6cedddffeb..2ac77c4776 100644 --- a/engines/zvision/scripting/controls/save_control.cpp +++ b/engines/zvision/scripting/controls/save_control.cpp @@ -102,9 +102,7 @@ bool SaveControl::process(uint32 deltaTimeInMillis) { toSave = false; if (toSave) { - // FIXME: At this point, the screen shows the save control, so the save game thumbnails will always - // show the save control - _engine->getSaveManager()->saveGameBuffered(iter->saveId, inp->getText()); + _engine->getSaveManager()->saveGame(iter->saveId, inp->getText(), true); _engine->getRenderManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); _engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation()); } diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp index 227c43557c..b4da61a119 100644 --- a/engines/zvision/scripting/scr_file_handling.cpp +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -270,6 +270,8 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis // Only used by Zork: Nemesis actionList.push_back(new ActionRegion(_engine, slot, args)); } else if (act.matchString("restore_game", true)) { + // Only used by ZGI to load the restart game slot, r.svr. + // Used by the credits screen. actionList.push_back(new ActionRestoreGame(_engine, slot, args)); } else if (act.matchString("rotate_to", true)) { actionList.push_back(new ActionRotateTo(_engine, slot, args)); diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index 464e8bfe4d..ad049434c3 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -500,7 +500,7 @@ void ScriptManager::changeLocation(char _world, char _room, char _node, char _vi _nextLocation.node = _node; _nextLocation.view = _view; _nextLocation.offset = offset; - // If next location 0000 - it's indicate to go to previous location. + // If next location is 0000, return to the previous location. if (_nextLocation.world == '0' && _nextLocation.room == '0' && _nextLocation.node == '0' && _nextLocation.view == '0') { if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { _nextLocation.world = getStateValue(StateKey_LastWorld); diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h index 78c1b77dea..f6201c3572 100644 --- a/engines/zvision/scripting/script_manager.h +++ b/engines/zvision/scripting/script_manager.h @@ -87,6 +87,7 @@ enum StateKey { StateKey_JapanFonts = 75, StateKey_ExecScopeStyle = 76, StateKey_Brightness = 77, + StateKey_MPEGMovies = 78, StateKey_EF9_R = 91, StateKey_EF9_G = 92, StateKey_EF9_B = 93, diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp index 6d1980b1af..0ef5de2f55 100644 --- a/engines/zvision/sound/zork_raw.cpp +++ b/engines/zvision/sound/zork_raw.cpp @@ -242,11 +242,27 @@ Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stre Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { Common::File *file = new Common::File(); - if (!engine->getSearchManager()->openFile(*file, filePath)) - error("File not found: %s", filePath.c_str()); + Common::String actualName = filePath; + bool found = engine->getSearchManager()->openFile(*file, actualName); + bool isRaw = actualName.hasSuffix(".raw"); + + if ((!found && isRaw) || (found && isRaw && file->size() < 10)) { + if (found) + file->close(); + + // Check for an audio patch (.src) + actualName.setChar('s', actualName.size() - 3); + actualName.setChar('r', actualName.size() - 2); + actualName.setChar('c', actualName.size() - 1); + + if (!engine->getSearchManager()->openFile(*file, actualName)) + error("File not found: %s", actualName.c_str()); + } else if (!found && !isRaw) { + error("File not found: %s", actualName.c_str()); + } // Get the file name - Common::StringTokenizer tokenizer(filePath, "/\\"); + Common::StringTokenizer tokenizer(actualName, "/\\"); Common::String fileName; while (!tokenizer.empty()) { fileName = tokenizer.nextToken(); diff --git a/engines/zvision/text/string_manager.cpp b/engines/zvision/text/string_manager.cpp index 6b870d0b8d..c62e18f4b0 100644 --- a/engines/zvision/text/string_manager.cpp +++ b/engines/zvision/text/string_manager.cpp @@ -51,9 +51,8 @@ void StringManager::initialize(ZVisionGameId gameId) { void StringManager::loadStrFile(const Common::String &fileName) { Common::File file; - if (!_engine->getSearchManager()->openFile(file, fileName)) { + if (!_engine->getSearchManager()->openFile(file, fileName)) error("%s does not exist. String parsing failed", fileName.c_str()); - } uint lineNumber = 0; while (!file.eos()) { diff --git a/engines/zvision/text/subtitles.cpp b/engines/zvision/text/subtitles.cpp index acf4c37c2f..ffc9e2b808 100644 --- a/engines/zvision/text/subtitles.cpp +++ b/engines/zvision/text/subtitles.cpp @@ -27,7 +27,7 @@ namespace ZVision { -Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : +Subtitle::Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires) : _engine(engine), _areaId(-1), _subId(-1) { @@ -44,6 +44,8 @@ Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : int32 x1, y1, x2, y2; sscanf(str.c_str(), "%*[^:]:%d %d %d %d", &x1, &y1, &x2, &y2); Common::Rect rct = Common::Rect(x1, y1, x2, y2); + if (upscaleToHires) + _engine->getRenderManager()->upscaleRect(rct); _areaId = _engine->getRenderManager()->createSubArea(rct); } else if (str.matchString("*TextFile*", true)) { char filename[64]; @@ -67,6 +69,11 @@ Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : int32 sb; if (sscanf(str.c_str(), "%*[^:]:(%d,%d)=%d", &st, &en, &sb) == 3) { if (sb <= (int32)_subs.size()) { + if (upscaleToHires) { + // Convert from 15FPS (AVI) to 29.97FPS (VOB) + st = st * 2997 / 1500; + en = en * 2997 / 1500; + } _subs[sb].start = st; _subs[sb].stop = en; } diff --git a/engines/zvision/text/subtitles.h b/engines/zvision/text/subtitles.h index c3da6583a4..329339be55 100644 --- a/engines/zvision/text/subtitles.h +++ b/engines/zvision/text/subtitles.h @@ -31,7 +31,7 @@ class ZVision; class Subtitle { public: - Subtitle(ZVision *engine, const Common::String &subname); + Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires = false); ~Subtitle(); void process(int32 time); diff --git a/engines/zvision/video/video.cpp b/engines/zvision/video/video.cpp index 0913b28818..d5ffbeb536 100644 --- a/engines/zvision/video/video.cpp +++ b/engines/zvision/video/video.cpp @@ -23,13 +23,16 @@ #include "common/scummsys.h" #include "common/system.h" #include "video/video_decoder.h" +#ifdef USE_MPEG2 +#include "video/mpegps_decoder.h" +#endif #include "engines/util.h" #include "graphics/surface.h" #include "zvision/zvision.h" #include "zvision/core/clock.h" #include "zvision/graphics/render_manager.h" -#include "zvision/scripting//script_manager.h" +#include "zvision/scripting/script_manager.h" #include "zvision/text/subtitles.h" #include "zvision/video/rlf_decoder.h" #include "zvision/video/zork_avi_decoder.h" @@ -45,6 +48,10 @@ Video::VideoDecoder *ZVision::loadAnimation(const Common::String &fileName) { animation = new RLFDecoder(); else if (tmpFileName.hasSuffix(".avi")) animation = new ZorkAVIDecoder(); +#ifdef USE_MPEG2 + else if (tmpFileName.hasSuffix(".vob")) + animation = new Video::MPEGPSDecoder(); +#endif else error("Unknown suffix for animation %s", fileName.c_str()); diff --git a/engines/zvision/zvision.cpp b/engines/zvision/zvision.cpp index 54991aced3..b42906fef3 100644 --- a/engines/zvision/zvision.cpp +++ b/engines/zvision/zvision.cpp @@ -52,7 +52,7 @@ namespace ZVision { -#define ZVISION_SETTINGS_KEYS_COUNT 11 +#define ZVISION_SETTINGS_KEYS_COUNT 12 struct zvisionIniSettings { const char *name; @@ -73,7 +73,8 @@ struct zvisionIniSettings { {"panarotatespeed", StateKey_RotateSpeed, 540, false, true}, // checked by universe.scr {"noanimwhileturning", StateKey_NoTurnAnim, -1, false, true}, // toggle playing animations during pana rotation {"venusenabled", StateKey_VenusEnable, -1, true, true}, - {"subtitles", StateKey_Subtitles, -1, true, true} + {"subtitles", StateKey_Subtitles, -1, true, true}, + {"mpegmovies", StateKey_MPEGMovies, -1, true, true} // Zork: Grand Inquisitor DVD hi-res MPEG movies (0 = normal, 1 = hires, 2 = disable option) }; ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) @@ -105,15 +106,6 @@ ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) debug(1, "ZVision::ZVision"); - uint16 workingWindowWidth = (gameDesc->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_WIDTH : ZGI_WORKING_WINDOW_WIDTH; - uint16 workingWindowHeight = (gameDesc->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_HEIGHT : ZGI_WORKING_WINDOW_HEIGHT; - _workingWindow = Common::Rect( - (WINDOW_WIDTH - workingWindowWidth) / 2, - (WINDOW_HEIGHT - workingWindowHeight) / 2, - ((WINDOW_WIDTH - workingWindowWidth) / 2) + workingWindowWidth, - ((WINDOW_HEIGHT - workingWindowHeight) / 2) + workingWindowHeight - ); - memset(_cheatBuffer, 0, sizeof(_cheatBuffer)); } @@ -193,25 +185,17 @@ void ZVision::initialize() { _searchManager->addDir("addon"); if (_gameDescription->gameId == GID_GRANDINQUISITOR) { - _searchManager->loadZix("INQUIS.ZIX"); - _searchManager->addPatch("C000H01Q.RAW", "C000H01Q.SRC"); - _searchManager->addPatch("CM00H01Q.RAW", "CM00H01Q.SRC"); - _searchManager->addPatch("DM00H01Q.RAW", "DM00H01Q.SRC"); - _searchManager->addPatch("E000H01Q.RAW", "E000H01Q.SRC"); - _searchManager->addPatch("EM00H50Q.RAW", "EM00H50Q.SRC"); - _searchManager->addPatch("GJNPH65P.RAW", "GJNPH65P.SRC"); - _searchManager->addPatch("GJNPH72P.RAW", "GJNPH72P.SRC"); - _searchManager->addPatch("H000H01Q.RAW", "H000H01Q.SRC"); - _searchManager->addPatch("M000H01Q.RAW", "M000H01Q.SRC"); - _searchManager->addPatch("P000H01Q.RAW", "P000H01Q.SRC"); - _searchManager->addPatch("Q000H01Q.RAW", "Q000H01Q.SRC"); - _searchManager->addPatch("SW00H01Q.RAW", "SW00H01Q.SRC"); - _searchManager->addPatch("T000H01Q.RAW", "T000H01Q.SRC"); - _searchManager->addPatch("U000H01Q.RAW", "U000H01Q.SRC"); - } else if (_gameDescription->gameId == GID_NEMESIS) - _searchManager->loadZix("NEMESIS.ZIX"); + if (!_searchManager->loadZix("INQUIS.ZIX")) + error("Unable to load the game ZIX file"); + } else if (_gameDescription->gameId == GID_NEMESIS) { + if (!_searchManager->loadZix("NEMESIS.ZIX")) { + // The game might not be installed, try MEDIUM.ZIX instead + if (!_searchManager->loadZix("ZNEMSCR/MEDIUM.ZIX")) + error("Unable to load the game ZIX file"); + } + } - initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_screenPixelFormat); + initScreen(); // Register random source _rnd = new Common::RandomSource("zvision"); @@ -239,6 +223,11 @@ void ZVision::initialize() { loadSettings(); +#ifndef USE_MPEG2 + // libmpeg2 not loaded, disable the MPEG2 movies option + _scriptManager->setStateValue(StateKey_MPEGMovies, 2); +#endif + // Create debugger console. It requires GFX to be initialized _console = new Console(this); _doubleFPS = ConfMan.getBool("doublefps"); @@ -358,4 +347,23 @@ void ZVision::fpsTimer() { _renderedFrameCount = 0; } +void ZVision::initScreen() { + uint16 workingWindowWidth = (_gameDescription->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_WIDTH : ZGI_WORKING_WINDOW_WIDTH; + uint16 workingWindowHeight = (_gameDescription->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_HEIGHT : ZGI_WORKING_WINDOW_HEIGHT; + _workingWindow = Common::Rect( + (WINDOW_WIDTH - workingWindowWidth) / 2, + (WINDOW_HEIGHT - workingWindowHeight) / 2, + ((WINDOW_WIDTH - workingWindowWidth) / 2) + workingWindowWidth, + ((WINDOW_HEIGHT - workingWindowHeight) / 2) + workingWindowHeight + ); + + initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_screenPixelFormat); +} + +void ZVision::initHiresScreen() { + _renderManager->upscaleRect(_workingWindow); + + initGraphics(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, true, &_screenPixelFormat); +} + } // End of namespace ZVision diff --git a/engines/zvision/zvision.h b/engines/zvision/zvision.h index ad22ddaaa2..854cd77bb8 100644 --- a/engines/zvision/zvision.h +++ b/engines/zvision/zvision.h @@ -67,6 +67,27 @@ class TextRenderer; class Subtitle; class MidiManager; +enum { + WINDOW_WIDTH = 640, + WINDOW_HEIGHT = 480, + + HIRES_WINDOW_WIDTH = 800, + HIRES_WINDOW_HEIGHT = 600, + + // Zork nemesis working window sizes + ZNM_WORKING_WINDOW_WIDTH = 512, + ZNM_WORKING_WINDOW_HEIGHT = 320, + + // ZGI working window sizes + ZGI_WORKING_WINDOW_WIDTH = 640, + ZGI_WORKING_WINDOW_HEIGHT = 344, + + ROTATION_SCREEN_EDGE_OFFSET = 60, + MAX_ROTATION_SPEED = 400, // Pixels per second + + KEYBUF_SIZE = 20 +}; + class ZVision : public Engine { public: ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc); @@ -83,24 +104,6 @@ public: const Graphics::PixelFormat _screenPixelFormat; private: - enum { - WINDOW_WIDTH = 640, - WINDOW_HEIGHT = 480, - - // Zork nemesis working window sizes - ZNM_WORKING_WINDOW_WIDTH = 512, - ZNM_WORKING_WINDOW_HEIGHT = 320, - - // ZGI working window sizes - ZGI_WORKING_WINDOW_WIDTH = 640, - ZGI_WORKING_WINDOW_HEIGHT = 344, - - ROTATION_SCREEN_EDGE_OFFSET = 60, - MAX_ROTATION_SPEED = 400, // Pixels per second - - KEYBUF_SIZE = 20 - }; - Console *_console; const ZVisionGameDescription *_gameDescription; @@ -194,6 +197,9 @@ public: _clock.stop(); } + void initScreen(); + void initHiresScreen(); + /** * Play a video until it is finished. This is a blocking call. It will call * _clock.stop() when the video starts and _clock.start() when the video finishes. diff --git a/test/common/endian.h b/test/common/endian.h index cba7618c43..065b6997fc 100644 --- a/test/common/endian.h +++ b/test/common/endian.h @@ -10,6 +10,18 @@ class EndianTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(MKTAG('A','B','C','D'), tag); } + void test_READ_BE_UINT64() { + const byte data[8] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF}; + uint64 value = READ_BE_UINT64(data); + TS_ASSERT_EQUALS(value, 0x123456789ABCDEFFULL); + } + + void test_READ_LE_UINT64() { + const byte data[8] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF}; + uint64 value = READ_LE_UINT64(data); + TS_ASSERT_EQUALS(value, 0xFFDEBC9A78563412ULL); + } + void test_READ_BE_UINT32() { const char data[4] = { 0x12, 0x34, 0x56, 0x78 }; uint32 value = READ_BE_UINT32(data); diff --git a/test/common/memoryreadstream.h b/test/common/memoryreadstream.h index adef861a5e..3e1472f408 100644 --- a/test/common/memoryreadstream.h +++ b/test/common/memoryreadstream.h @@ -60,28 +60,32 @@ class MemoryReadStreamTestSuite : public CxxTest::TestSuite { } void test_seek_read_le() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStream ms(contents, sizeof(contents)); TS_ASSERT_EQUALS(ms.readUint16LE(), 0x0201UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32LE(), 0x06050403UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0E0D0C0B0A090807ULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_be() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStream ms(contents, sizeof(contents)); TS_ASSERT_EQUALS(ms.readUint16BE(), 0x0102UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32BE(), 0x03040506UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64BE(), 0x0708090A0B0C0D0EULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } diff --git a/test/common/memoryreadstreamendian.h b/test/common/memoryreadstreamendian.h index 35e804c70b..c25ec29e1a 100644 --- a/test/common/memoryreadstreamendian.h +++ b/test/common/memoryreadstreamendian.h @@ -60,54 +60,62 @@ class MemoryReadStreamEndianTestSuite : public CxxTest::TestSuite { } void test_seek_read_le() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), false); TS_ASSERT_EQUALS(ms.readUint16LE(), 0x0201UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32LE(), 0x06050403UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0E0D0C0B0A090807ULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_be() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), false); TS_ASSERT_EQUALS(ms.readUint16BE(), 0x0102UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32BE(), 0x03040506UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64BE(), 0x0708090A0B0C0D0EULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_le2() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), false); TS_ASSERT_EQUALS(ms.readUint16(), 0x0201UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32(), 0x06050403UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64(), 0x0E0D0C0B0A090807ULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_be2() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), true); TS_ASSERT_EQUALS(ms.readUint16(), 0x0102UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32(), 0x03040506UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64(), 0x0708090A0B0C0D0EULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } }; diff --git a/video/module.mk b/video/module.mk index 5754350e42..be014598f2 100644 --- a/video/module.mk +++ b/video/module.mk @@ -5,6 +5,7 @@ MODULE_OBJS := \ coktel_decoder.o \ dxa_decoder.o \ flic_decoder.o \ + mpegps_decoder.o \ psx_decoder.o \ qt_decoder.o \ smk_decoder.o \ diff --git a/video/mpegps_decoder.cpp b/video/mpegps_decoder.cpp new file mode 100644 index 0000000000..d8f7f5a68c --- /dev/null +++ b/video/mpegps_decoder.cpp @@ -0,0 +1,732 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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/raw.h" +#include "common/debug.h" +#include "common/endian.h" +#include "common/stream.h" +#include "common/system.h" +#include "common/textconsole.h" + +#include "video/mpegps_decoder.h" +#include "image/codecs/mpeg.h" + +// The demuxing code is based on libav's demuxing code + +namespace Video { + +enum { + kStartCodePack = 0x1BA, + kStartCodeSystemHeader = 0x1BB, + kStartCodeProgramStreamMap = 0x1BC, + kStartCodePrivateStream1 = 0x1BD, + kStartCodePaddingStream = 0x1BE, + kStartCodePrivateStream2 = 0x1BF +}; + +MPEGPSDecoder::MPEGPSDecoder() { + _stream = 0; + memset(_psmESType, 0, 256); +} + +MPEGPSDecoder::~MPEGPSDecoder() { + close(); +} + +bool MPEGPSDecoder::loadStream(Common::SeekableReadStream *stream) { + close(); + + _stream = stream; + + if (!addFirstVideoTrack()) { + close(); + return false; + } + + _stream->seek(0); + return true; +} + +void MPEGPSDecoder::close() { + VideoDecoder::close(); + + delete _stream; + _stream = 0; + + _streamMap.clear(); + + memset(_psmESType, 0, 256); +} + +void MPEGPSDecoder::readNextPacket() { + if (_stream->eos()) + return; + + for (;;) { + int32 startCode; + uint32 pts, dts; + int size = readNextPacketHeader(startCode, pts, dts); + + if (size < 0) { + // End of stream + for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + ((MPEGVideoTrack *)*it)->setEndOfTrack(); + return; + } + + MPEGStream *stream = 0; + Common::SeekableReadStream *packet = _stream->readStream(size); + + if (_streamMap.contains(startCode)) { + // We already found the stream + stream = _streamMap[startCode]; + } else { + // We haven't seen this before + + if (startCode == kStartCodePrivateStream1) { + PrivateStreamType streamType = detectPrivateStreamType(packet); + packet->seek(0); + + // TODO: Handling of these types (as needed) + + const char *typeName; + + switch (streamType) { + case kPrivateStreamAC3: + typeName = "AC-3"; + break; + case kPrivateStreamDTS: + typeName = "DTS"; + break; + case kPrivateStreamDVDPCM: + typeName = "DVD PCM"; + break; + case kPrivateStreamPS2Audio: + typeName = "PS2 Audio"; + break; + default: + typeName = "Unknown"; + break; + } + + warning("Unhandled DVD private stream: %s", typeName); + + // Make it 0 so we don't get the warning twice + _streamMap[startCode] = 0; + } else if (startCode >= 0x1E0 && startCode <= 0x1EF) { + // Video stream + // TODO: Multiple video streams + warning("Found extra video stream 0x%04X", startCode); + _streamMap[startCode] = 0; + } else if (startCode >= 0x1C0 && startCode <= 0x1DF) { +#ifdef USE_MAD + // MPEG Audio stream + MPEGAudioTrack *audioTrack = new MPEGAudioTrack(packet); + stream = audioTrack; + _streamMap[startCode] = audioTrack; + addTrack(audioTrack); +#else + warning("Found audio stream 0x%04X, but no MAD support compiled in", startCode); + _streamMap[startCode] = 0; +#endif + } else { + // Probably not relevant + debug(0, "Found unhandled MPEG-PS stream type 0x%04x", startCode); + _streamMap[startCode] = 0; + } + } + + if (stream) { + bool done = stream->sendPacket(packet, pts, dts); + + if (done && stream->getStreamType() == MPEGStream::kStreamTypeVideo) + return; + } else { + delete packet; + } + } +} + +#define MAX_SYNC_SIZE 100000 + +int MPEGPSDecoder::findNextStartCode(uint32 &size) { + size = MAX_SYNC_SIZE; + int32 state = 0xFF; + + while (size > 0) { + byte v = _stream->readByte(); + + if (_stream->eos()) + return -1; + + size--; + + if (state == 0x1) + return ((state << 8) | v) & 0xFFFFFF; + + state = ((state << 8) | v) & 0xFFFFFF; + } + + return -1; +} + +int MPEGPSDecoder::readNextPacketHeader(int32 &startCode, uint32 &pts, uint32 &dts) { + for (;;) { + uint32 size; + startCode = findNextStartCode(size); + + if (_stream->eos()) + return -1; + + if (startCode < 0) + continue; + + uint32 lastSync = _stream->pos(); + + if (startCode == kStartCodePack || startCode == kStartCodeSystemHeader) + continue; + + int length = _stream->readUint16BE(); + + if (startCode == kStartCodePaddingStream || startCode == kStartCodePrivateStream2) { + _stream->skip(length); + continue; + } + + if (startCode == kStartCodeProgramStreamMap) { + parseProgramStreamMap(length); + continue; + } + + // Find matching stream + if (!((startCode >= 0x1C0 && startCode <= 0x1DF) || + (startCode >= 0x1E0 && startCode <= 0x1EF) || + startCode == kStartCodePrivateStream1 || startCode == 0x1FD)) + continue; + + // Stuffing + byte c; + for (;;) { + if (length < 1) { + _stream->seek(lastSync); + continue; + } + + c = _stream->readByte(); + length--; + + // XXX: for mpeg1, should test only bit 7 + if (c != 0xFF) + break; + } + + if ((c & 0xC0) == 0x40) { + // Buffer scale and size + _stream->readByte(); + c = _stream->readByte(); + length -= 2; + } + + pts = 0xFFFFFFFF; + dts = 0xFFFFFFFF; + + if ((c & 0xE0) == 0x20) { + dts = pts = readPTS(c); + length -= 4; + + if (c & 0x10) { + dts = readPTS(-1); + length -= 5; + } + } else if ((c & 0xC0) == 0x80) { + // MPEG-2 PES + byte flags = _stream->readByte(); + int headerLength = _stream->readByte(); + length -= 2; + + if (headerLength > length) { + _stream->seek(lastSync); + continue; + } + + length -= headerLength; + + if (flags & 0x80) { + dts = pts = readPTS(-1); + headerLength -= 5; + + if (flags & 0x40) { + dts = readPTS(-1); + headerLength -= 5; + } + } + + if (flags & 0x3F && headerLength == 0) { + flags &= 0xC0; + warning("Further flags set but no bytes left"); + } + + if (flags & 0x01) { // PES extension + byte pesExt =_stream->readByte(); + headerLength--; + + // Skip PES private data, program packet sequence + int skip = (pesExt >> 4) & 0xB; + skip += skip & 0x9; + + if (pesExt & 0x40 || skip > headerLength) { + warning("pesExt %x is invalid", pesExt); + pesExt = skip = 0; + } else { + _stream->skip(skip); + headerLength -= skip; + } + + if (pesExt & 0x01) { // PES extension 2 + byte ext2Length = _stream->readByte(); + headerLength--; + + if ((ext2Length & 0x7F) != 0) { + byte idExt = _stream->readByte(); + + if ((idExt & 0x80) == 0) + startCode = (startCode & 0xFF) << 8; + + headerLength--; + } + } + } + + if (headerLength < 0) { + _stream->seek(lastSync); + continue; + } + + _stream->skip(headerLength); + } else if (c != 0xF) { + continue; + } + + if (length < 0) { + _stream->seek(lastSync); + continue; + } + + return length; + } +} + +uint32 MPEGPSDecoder::readPTS(int c) { + byte buf[5]; + + buf[0] = (c < 0) ? _stream->readByte() : c; + _stream->read(buf + 1, 4); + + return ((buf[0] & 0x0E) << 29) | ((READ_BE_UINT16(buf + 1) >> 1) << 15) | (READ_BE_UINT16(buf + 3) >> 1); +} + +void MPEGPSDecoder::parseProgramStreamMap(int length) { + _stream->readByte(); + _stream->readByte(); + + // skip program stream info + _stream->skip(_stream->readUint16BE()); + + int esMapLength = _stream->readUint16BE(); + + while (esMapLength >= 4) { + byte type = _stream->readByte(); + byte esID = _stream->readByte(); + uint16 esInfoLength = _stream->readUint16BE(); + + // Remember mapping from stream id to stream type + _psmESType[esID] = type; + + // Skip program stream info + _stream->skip(esInfoLength); + + esMapLength -= 4 + esInfoLength; + } + + _stream->readUint32BE(); // CRC32 +} + +bool MPEGPSDecoder::addFirstVideoTrack() { + for (;;) { + int32 startCode; + uint32 pts, dts; + int size = readNextPacketHeader(startCode, pts, dts); + + // End of stream? We failed + if (size < 0) + return false; + + if (startCode >= 0x1E0 && startCode <= 0x1EF) { + // Video stream + // Can be MPEG-1/2 or MPEG-4/h.264. We'll assume the former and + // I hope we never need the latter. + Common::SeekableReadStream *firstPacket = _stream->readStream(size); + MPEGVideoTrack *track = new MPEGVideoTrack(firstPacket, getDefaultHighColorFormat()); + addTrack(track); + _streamMap[startCode] = track; + delete firstPacket; + break; + } + + _stream->skip(size); + } + + return true; +} + +MPEGPSDecoder::PrivateStreamType MPEGPSDecoder::detectPrivateStreamType(Common::SeekableReadStream *packet) { + uint32 dvdCode = packet->readUint32LE(); + if (packet->eos()) + return kPrivateStreamUnknown; + + uint32 ps2Header = packet->readUint32BE(); + if (!packet->eos() && ps2Header == MKTAG('S', 'S', 'h', 'd')) + return kPrivateStreamPS2Audio; + + switch (dvdCode & 0xE0) { + case 0x80: + if ((dvdCode & 0xF8) == 0x88) + return kPrivateStreamDTS; + + return kPrivateStreamAC3; + case 0xA0: + return kPrivateStreamDVDPCM; + } + + return kPrivateStreamUnknown; +} + +MPEGPSDecoder::MPEGVideoTrack::MPEGVideoTrack(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format) { + _surface = 0; + _endOfTrack = false; + _curFrame = -1; + _nextFrameStartTime = Audio::Timestamp(0, 27000000); // 27 MHz timer + + findDimensions(firstPacket, format); + +#ifdef USE_MPEG2 + _mpegDecoder = new Image::MPEGDecoder(); +#endif +} + +MPEGPSDecoder::MPEGVideoTrack::~MPEGVideoTrack() { +#ifdef USE_MPEG2 + delete _mpegDecoder; +#endif + + if (_surface) { + _surface->free(); + delete _surface; + } +} + +uint16 MPEGPSDecoder::MPEGVideoTrack::getWidth() const { + return _surface ? _surface->w : 0; +} + +uint16 MPEGPSDecoder::MPEGVideoTrack::getHeight() const { + return _surface ? _surface->h : 0; +} + +Graphics::PixelFormat MPEGPSDecoder::MPEGVideoTrack::getPixelFormat() const { + if (!_surface) + return Graphics::PixelFormat(); + + return _surface->format; +} + +const Graphics::Surface *MPEGPSDecoder::MPEGVideoTrack::decodeNextFrame() { + return _surface; +} + +bool MPEGPSDecoder::MPEGVideoTrack::sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts) { +#ifdef USE_MPEG2 + uint32 framePeriod; + bool foundFrame = _mpegDecoder->decodePacket(*packet, framePeriod, _surface); + + if (foundFrame) { + _curFrame++; + _nextFrameStartTime = _nextFrameStartTime.addFrames(framePeriod); + } +#endif + + delete packet; + +#ifdef USE_MPEG2 + return foundFrame; +#else + return true; +#endif +} + +void MPEGPSDecoder::MPEGVideoTrack::findDimensions(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format) { + // First, check for the picture start code + if (firstPacket->readUint32BE() != 0x1B3) + error("Failed to detect MPEG sequence start"); + + // This is part of the bitstream, but there's really no purpose + // to use Common::BitStream just for this: 12 bits width, 12 bits + // height + uint16 width = firstPacket->readByte() << 4; + uint16 height = firstPacket->readByte(); + width |= (height & 0xF0) >> 4; + height = ((height & 0x0F) << 8) | firstPacket->readByte(); + + debug(0, "MPEG dimensions: %dx%d", width, height); + + _surface = new Graphics::Surface(); + _surface->create(width, height, format); + + firstPacket->seek(0); +} + +#ifdef USE_MAD + +// The audio code here is almost entirely based on what we do in mp3.cpp + +MPEGPSDecoder::MPEGAudioTrack::MPEGAudioTrack(Common::SeekableReadStream *firstPacket) { + // The MAD_BUFFER_GUARD must always contain zeros (the reason + // for this is that the Layer III Huffman decoder of libMAD + // may read a few bytes beyond the end of the input buffer). + memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD); + + _state = MP3_STATE_INIT; + _audStream = 0; + + // Find out our audio parameters + initStream(firstPacket); + + while (_state != MP3_STATE_EOS) + readHeader(firstPacket); + + _audStream = Audio::makeQueuingAudioStream(_frame.header.samplerate, MAD_NCHANNELS(&_frame.header) == 2); + + deinitStream(); + + firstPacket->seek(0); + _state = MP3_STATE_INIT; +} + +MPEGPSDecoder::MPEGAudioTrack::~MPEGAudioTrack() { + deinitStream(); + delete _audStream; +} + +static inline int scaleSample(mad_fixed_t sample) { + // round + sample += (1L << (MAD_F_FRACBITS - 16)); + + // clip + if (sample > MAD_F_ONE - 1) + sample = MAD_F_ONE - 1; + else if (sample < -MAD_F_ONE) + sample = -MAD_F_ONE; + + // quantize and scale to not saturate when mixing a lot of channels + return sample >> (MAD_F_FRACBITS + 1 - 16); +} + +bool MPEGPSDecoder::MPEGAudioTrack::sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts) { + while (_state != MP3_STATE_EOS) + decodeMP3Data(packet); + + _state = MP3_STATE_READY; + delete packet; + return true; +} + +Audio::AudioStream *MPEGPSDecoder::MPEGAudioTrack::getAudioStream() const { + return _audStream; +} + +void MPEGPSDecoder::MPEGAudioTrack::initStream(Common::SeekableReadStream *packet) { + if (_state != MP3_STATE_INIT) + deinitStream(); + + // Init MAD + mad_stream_init(&_stream); + mad_frame_init(&_frame); + mad_synth_init(&_synth); + + // Reset the stream data + packet->seek(0, SEEK_SET); + + // Update state + _state = MP3_STATE_READY; + + // Read the first few sample bytes + readMP3Data(packet); +} + +void MPEGPSDecoder::MPEGAudioTrack::deinitStream() { + if (_state == MP3_STATE_INIT) + return; + + // Deinit MAD + mad_synth_finish(&_synth); + mad_frame_finish(&_frame); + mad_stream_finish(&_stream); + + _state = MP3_STATE_EOS; +} + +void MPEGPSDecoder::MPEGAudioTrack::readMP3Data(Common::SeekableReadStream *packet) { + uint32 remaining = 0; + + // Give up immediately if we already used up all data in the stream + if (packet->eos()) { + _state = MP3_STATE_EOS; + return; + } + + if (_stream.next_frame) { + // If there is still data in the MAD stream, we need to preserve it. + // Note that we use memmove, as we are reusing the same buffer, + // and hence the data regions we copy from and to may overlap. + remaining = _stream.bufend - _stream.next_frame; + assert(remaining < BUFFER_SIZE); // Paranoia check + memmove(_buf, _stream.next_frame, remaining); + } + + memset(_buf + remaining, 0, BUFFER_SIZE - remaining); + + // Try to read the next block + uint32 size = packet->read(_buf + remaining, BUFFER_SIZE - remaining); + if (size == 0) { + _state = MP3_STATE_EOS; + return; + } + + // Feed the data we just read into the stream decoder + _stream.error = MAD_ERROR_NONE; + mad_stream_buffer(&_stream, _buf, size + remaining); +} + +void MPEGPSDecoder::MPEGAudioTrack::readHeader(Common::SeekableReadStream *packet) { + if (_state != MP3_STATE_READY) + return; + + // If necessary, load more data into the stream decoder + if (_stream.error == MAD_ERROR_BUFLEN) + readMP3Data(packet); + + while (_state != MP3_STATE_EOS) { + _stream.error = MAD_ERROR_NONE; + + // Decode the next header. Note: mad_frame_decode would do this for us, too. + // However, for seeking we don't want to decode the full frame (else it would + // be far too slow). Hence we perform this explicitly in a separate step. + if (mad_header_decode(&_frame.header, &_stream) == -1) { + if (_stream.error == MAD_ERROR_BUFLEN) { + readMP3Data(packet); // Read more data + continue; + } else if (MAD_RECOVERABLE(_stream.error)) { + debug(6, "MPEGAudioTrack::readHeader(): Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); + continue; + } else { + warning("MPEGAudioTrack::readHeader(): Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); + break; + } + } + + break; + } + + if (_stream.error != MAD_ERROR_NONE) + _state = MP3_STATE_EOS; +} + +void MPEGPSDecoder::MPEGAudioTrack::decodeMP3Data(Common::SeekableReadStream *packet) { + if (_state == MP3_STATE_INIT) + initStream(packet); + + if (_state == MP3_STATE_EOS) + return; + + do { + // If necessary, load more data into the stream decoder + if (_stream.error == MAD_ERROR_BUFLEN) + readMP3Data(packet); + + while (_state == MP3_STATE_READY) { + _stream.error = MAD_ERROR_NONE; + + // Decode the next frame + if (mad_frame_decode(&_frame, &_stream) == -1) { + if (_stream.error == MAD_ERROR_BUFLEN) { + break; // Read more data + } else if (MAD_RECOVERABLE(_stream.error)) { + // Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here. + // These are normal and expected (caused by our frame skipping (i.e. "seeking") + // code above). + debug(6, "MPEGAudioTrack::decodeMP3Data(): Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream)); + continue; + } else { + warning("MPEGAudioTrack::decodeMP3Data(): Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream)); + break; + } + } + + // Synthesize PCM data + mad_synth_frame(&_synth, &_frame); + + // Output it to our queue + if (_synth.pcm.length != 0) { + byte *buffer = (byte *)malloc(_synth.pcm.length * 2 * MAD_NCHANNELS(&_frame.header)); + int16 *ptr = (int16 *)buffer; + + for (int i = 0; i < _synth.pcm.length; i++) { + *ptr++ = (int16)scaleSample(_synth.pcm.samples[0][i]); + + if (MAD_NCHANNELS(&_frame.header) == 2) + *ptr++ = (int16)scaleSample(_synth.pcm.samples[1][i]); + } + + int flags = Audio::FLAG_16BITS; + + if (_audStream->isStereo()) + flags |= Audio::FLAG_STEREO; + +#ifdef SCUMM_LITTLE_ENDIAN + flags |= Audio::FLAG_LITTLE_ENDIAN; +#endif + + _audStream->queueBuffer(buffer, _synth.pcm.length * 2 * MAD_NCHANNELS(&_frame.header), DisposeAfterUse::YES, flags); + } + break; + } + } while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN); + + if (_stream.error != MAD_ERROR_NONE) + _state = MP3_STATE_EOS; +} + +#endif + +} // End of namespace Video diff --git a/video/mpegps_decoder.h b/video/mpegps_decoder.h new file mode 100644 index 0000000000..0184d6f9ba --- /dev/null +++ b/video/mpegps_decoder.h @@ -0,0 +1,189 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public 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 VIDEO_MPEGPS_DECODER_H +#define VIDEO_MPEGPS_DECODER_H + +#include "common/hashmap.h" +#include "graphics/surface.h" +#include "video/video_decoder.h" + +#ifdef USE_MAD +#include <mad.h> +#endif + +namespace Audio { +class QueuingAudioStream; +} + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { +struct PixelFormat; +} + +namespace Image { +class MPEGDecoder; +} + +namespace Video { + +/** + * Decoder for MPEG Program Stream videos. + * Video decoder used in engines: + * - zvision + */ +class MPEGPSDecoder : public VideoDecoder { +public: + MPEGPSDecoder(); + virtual ~MPEGPSDecoder(); + + bool loadStream(Common::SeekableReadStream *stream); + void close(); + +protected: + void readNextPacket(); + bool useAudioSync() const { return false; } + +private: + // Base class for handling MPEG streams + class MPEGStream { + public: + virtual ~MPEGStream() {} + + enum StreamType { + kStreamTypeVideo, + kStreamTypeAudio + }; + + virtual bool sendPacket(Common::SeekableReadStream *firstPacket, uint32 pts, uint32 dts) = 0; + virtual StreamType getStreamType() const = 0; + }; + + // An MPEG 1/2 video track + class MPEGVideoTrack : public VideoTrack, public MPEGStream { + public: + MPEGVideoTrack(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format); + ~MPEGVideoTrack(); + + bool endOfTrack() const { return _endOfTrack; } + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + uint32 getNextFrameStartTime() const { return _nextFrameStartTime.msecs(); } + const Graphics::Surface *decodeNextFrame(); + + bool sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts); + StreamType getStreamType() const { return kStreamTypeVideo; } + + void setEndOfTrack() { _endOfTrack = true; } + + private: + bool _endOfTrack; + int _curFrame; + Audio::Timestamp _nextFrameStartTime; + Graphics::Surface *_surface; + + void findDimensions(Common::SeekableReadStream *firstPacket, const Graphics::PixelFormat &format); + +#ifdef USE_MPEG2 + Image::MPEGDecoder *_mpegDecoder; +#endif + }; + +#ifdef USE_MAD + // An MPEG audio track + // TODO: Merge this with the normal MP3Stream somehow + class MPEGAudioTrack : public AudioTrack, public MPEGStream { + public: + MPEGAudioTrack(Common::SeekableReadStream *firstPacket); + ~MPEGAudioTrack(); + + bool sendPacket(Common::SeekableReadStream *packet, uint32 pts, uint32 dts); + StreamType getStreamType() const { return kStreamTypeAudio; } + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::QueuingAudioStream *_audStream; + + enum State { + MP3_STATE_INIT, // Need to init the decoder + MP3_STATE_READY, // ready for processing data + MP3_STATE_EOS // end of data reached (may need to loop) + }; + + State _state; + + mad_stream _stream; + mad_frame _frame; + mad_synth _synth; + + enum { + BUFFER_SIZE = 5 * 8192 + }; + + // This buffer contains a slab of input data + byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD]; + + void initStream(Common::SeekableReadStream *packet); + void deinitStream(); + void readMP3Data(Common::SeekableReadStream *packet); + void readHeader(Common::SeekableReadStream *packet); + void decodeMP3Data(Common::SeekableReadStream *packet); + }; +#endif + + // The different types of private streams we can detect at the moment + enum PrivateStreamType { + kPrivateStreamUnknown, + kPrivateStreamAC3, + kPrivateStreamDTS, + kPrivateStreamDVDPCM, + kPrivateStreamPS2Audio + }; + + PrivateStreamType detectPrivateStreamType(Common::SeekableReadStream *packet); + + bool addFirstVideoTrack(); + + int readNextPacketHeader(int32 &startCode, uint32 &pts, uint32 &dts); + int findNextStartCode(uint32 &size); + uint32 readPTS(int c); + + void parseProgramStreamMap(int length); + byte _psmESType[256]; + + // A map from stream types to stream handlers + typedef Common::HashMap<int, MPEGStream *> StreamMap; + StreamMap _streamMap; + + Common::SeekableReadStream *_stream; +}; + +} // End of namespace Video + +#endif diff --git a/video/theora_decoder.cpp b/video/theora_decoder.cpp index cb6289bd60..ba596c6032 100644 --- a/video/theora_decoder.cpp +++ b/video/theora_decoder.cpp @@ -360,7 +360,11 @@ static double rint(double v) { } bool TheoraDecoder::VorbisAudioTrack::decodeSamples() { +#ifdef USE_TREMOR + ogg_int32_t **pcm; +#else float **pcm; +#endif // if there's pending, decoded audio, grab it int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm); diff --git a/video/theora_decoder.h b/video/theora_decoder.h index feb4c6b49e..5b683cf6af 100644 --- a/video/theora_decoder.h +++ b/video/theora_decoder.h @@ -33,7 +33,12 @@ #include "graphics/surface.h" #include <theora/theoradec.h> + +#ifdef USE_TREMOR +#include <tremor/ivorbiscodec.h> +#else #include <vorbis/codec.h> +#endif namespace Common { class SeekableReadStream; |