diff options
author | Vicent Marti | 2008-08-16 00:08:12 +0000 |
---|---|---|
committer | Vicent Marti | 2008-08-16 00:08:12 +0000 |
commit | 146be8e16b0abd43f87274a305d9d65b594d1d7f (patch) | |
tree | 8c019cf6c35345de29946d681102cd036876b425 /engines | |
parent | 2ae7dc0e32342a0b770bc67883f9b5b77967cb71 (diff) | |
parent | f2111eeb45dc8c41afb3e63bf3b86a09bf9a3532 (diff) | |
download | scummvm-rg350-146be8e16b0abd43f87274a305d9d65b594d1d7f.tar.gz scummvm-rg350-146be8e16b0abd43f87274a305d9d65b594d1d7f.tar.bz2 scummvm-rg350-146be8e16b0abd43f87274a305d9d65b594d1d7f.zip |
Merged revisions 33490,33492,33495-33496,33509-33512,33518-33519,33522-33527,33529-33530,33537,33541,33544,33546,33550,33552-33554,33556,33558,33561-33562,33565,33568,33570,33574,33576,33578-33581,33584-33587,33590,33596,33604-33611,33614-33615,33617-33618,33620-33621,33623,33626-33627,33632-33633,33635,33637,33639-33640,33642-33645,33648,33654-33655,33664,33667-33670,33673-33674,33678,33682,33686-33691,33693,33696,33698,33700,33703,33708,33710,33712-33714,33716,33719,33721-33723,33725-33727,33729-33730,33733,33736,33742,33754,33756,33758,33761,33763,33766,33777,33781-33788,33790,33792-33793,33795,33797,33805,33807-33812,33815-33817,33819,33822,33826,33829,33837,33839,33844,33847,33858-33861,33864,33871-33873,33875,33877-33879,33886,33889-33892,33894,33896,33900,33902-33903,33919 via svnmerge from
https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk
........
r33490 | thebluegr | 2008-08-01 16:03:20 +0200 (Fri, 01 Aug 2008) | 2 lines
Added load confirmation dialog for IHNM, like in the original.
Fixes bug #1971699 - "IHNM: No Load Game Confirmation"
........
r33492 | thebluegr | 2008-08-01 16:27:40 +0200 (Fri, 01 Aug 2008) | 1 line
Fixed crash on exit from commit #32742 - the memory leak was only in IHNM, not ITE (in ITE, the 4 sprite lists are actually one)
........
r33495 | fingolfin | 2008-08-01 18:31:40 +0200 (Fri, 01 Aug 2008) | 1 line
Print a warning if unsupported XMIDI controllers are used
........
r33496 | fingolfin | 2008-08-01 18:33:22 +0200 (Fri, 01 Aug 2008) | 1 line
Moved Ludde & yaz0r to a new credits section, retired project leaders
........
r33509 | peres001 | 2008-08-02 04:24:36 +0200 (Sat, 02 Aug 2008) | 2 lines
* Added a preprocessor to deal with the crappy location scripts in BRA.
* Added some comments on how the parser and related code should be changed to make things smoother.
........
r33510 | peres001 | 2008-08-02 04:26:09 +0200 (Sat, 02 Aug 2008) | 1 line
Removed debug code I forgot a long time ago ;)
........
r33511 | peres001 | 2008-08-02 04:43:05 +0200 (Sat, 02 Aug 2008) | 1 line
Fixed issues detected by DrMcCoy's obnoxious compiler. ;)
........
r33512 | peres001 | 2008-08-02 05:19:45 +0200 (Sat, 02 Aug 2008) | 2 lines
* Increasing max number of allowed token on one line.
* Increasing buffer size to match old parser.
........
r33518 | lordhoto | 2008-08-02 13:40:09 +0200 (Sat, 02 Aug 2008) | 2 lines
Fix for bug #2035459 "KYRA1: long savegame names cause garbage".
........
r33519 | lordhoto | 2008-08-02 13:57:06 +0200 (Sat, 02 Aug 2008) | 2 lines
Proper fix for bug #2035459 "KYRA1: long savegame names cause garbage".
........
r33522 | lordhoto | 2008-08-02 16:32:05 +0200 (Sat, 02 Aug 2008) | 2 lines
Fixed regressions in kyra1 amiga.
........
r33523 | athrxx | 2008-08-02 16:35:38 +0200 (Sat, 02 Aug 2008) | 1 line
HOF: bug fix for FM-Towns music when using output rates other than 22050 khz
........
r33524 | lordhoto | 2008-08-02 16:35:43 +0200 (Sat, 02 Aug 2008) | 2 lines
Add warning when a voc file is not found.
........
r33525 | Kirben | 2008-08-02 16:37:10 +0200 (Sat, 02 Aug 2008) | 1 line
Detect both HOF non-interactive demos.
........
r33526 | lordhoto | 2008-08-02 16:42:44 +0200 (Sat, 02 Aug 2008) | 2 lines
Fixed yet another crash in kyra1 amiga.
........
r33527 | lordhoto | 2008-08-02 17:05:19 +0200 (Sat, 02 Aug 2008) | 2 lines
Added a member function loadSoundFile to Sound which accepts a filename string instead of a filename list id.
........
r33529 | buddha_ | 2008-08-02 17:47:25 +0200 (Sat, 02 Aug 2008) | 7 lines
- Combined loadAbs and loadResource into one function.
- Made resource loading functions (loadSpl, loadMsk etc)
return the number of the animDataTable entry right after
the loaded data.
- Made resource loading functions always load multiframe
data into directly sequential animDataTable entries
(Hopefully this won't break anything).
........
r33530 | buddha_ | 2008-08-02 17:59:38 +0200 (Sat, 02 Aug 2008) | 3 lines
Made loadResourcesFromSave internally use the loadResource-function.
Fixes Operation Stealth savegame loading! HURRAH! FINALLY!
........
r33537 | sev | 2008-08-02 23:05:46 +0200 (Sat, 02 Aug 2008) | 2 lines
Fix crash when drascula.dat file is missing
........
r33541 | sev | 2008-08-02 23:22:05 +0200 (Sat, 02 Aug 2008) | 2 lines
Fix bug #2007170: "DRASCULA: No Spaces in SaveGames Names"
........
r33544 | sev | 2008-08-02 23:36:08 +0200 (Sat, 02 Aug 2008) | 2 lines
Fix bug #2023727: "MONKEY2: Misplaced Text"
........
r33546 | sev | 2008-08-02 23:57:35 +0200 (Sat, 02 Aug 2008) | 2 lines
Fix bug #1879606: "MANIACNES: Crash when game ends"
........
r33550 | fingolfin | 2008-08-03 00:32:32 +0200 (Sun, 03 Aug 2008) | 1 line
Fix for bug #1972625 'ALL: On-the-fly targets are written to the config file'
........
r33552 | sev | 2008-08-03 00:51:53 +0200 (Sun, 03 Aug 2008) | 3 lines
Fix for bug #1945335: "SCUMM: Invalid charset id can be stored".
Regressions possible.
........
r33553 | fingolfin | 2008-08-03 01:01:14 +0200 (Sun, 03 Aug 2008) | 1 line
Renamed Tinsel v1->v0 (used in the demo only), and v2->v1, to avoid confusion with DW2
........
r33554 | sev | 2008-08-03 01:05:38 +0200 (Sun, 03 Aug 2008) | 2 lines
Fix bug #2035390: "GUI: setting game id to "scummvm" messes up list"
........
r33556 | athrxx | 2008-08-03 01:11:31 +0200 (Sun, 03 Aug 2008) | 1 line
add non-interactive lol demo
........
r33558 | eriktorbjorn | 2008-08-03 09:40:04 +0200 (Sun, 03 Aug 2008) | 3 lines
Fixed #2021902 ("DRASCULA: Can't adjust the volume inside the game"). I can't
verify that this matches the original behaviour, but at least it's better now.
........
r33561 | buddha_ | 2008-08-03 10:01:11 +0200 (Sun, 03 Aug 2008) | 1 line
Shut up MSVC warning about mgY being an unreferenced local variable.
........
r33562 | fingolfin | 2008-08-03 10:05:19 +0200 (Sun, 03 Aug 2008) | 1 line
Proper fix for bug #2035390: Check for ConfigManager::kApplicationDomain, and also forbid targets starting with an underscore
........
r33565 | eriktorbjorn | 2008-08-03 11:41:10 +0200 (Sun, 03 Aug 2008) | 4 lines
Fixed bug that prevented upper-case letters from being used in savegame names.
Apparently, strchr(..., 0) will find the string terminator - at least for me -
and when that's added to the name, it will terminate the string.
........
r33568 | eriktorbjorn | 2008-08-03 11:51:13 +0200 (Sun, 03 Aug 2008) | 2 lines
Fixed bug #1995022 ("BASS: wrong displayed 2 line text").
........
r33570 | eriktorbjorn | 2008-08-03 12:16:17 +0200 (Sun, 03 Aug 2008) | 5 lines
Fixed bug #1995033 ("BASS: BG Sound stopped on opening item list"). Apparently
the sound is supposed to be paused and then unpaused, but the pause function is
called many more times than the unpause function. In the original, this
presumably didn't matter. In ScummVM's mixer, it does.
........
r33574 | sev | 2008-08-03 13:42:05 +0200 (Sun, 03 Aug 2008) | 2 lines
Fix bug #2017432: "DRASCULA: Typing is slow when you save a game"
........
r33576 | sev | 2008-08-03 14:02:09 +0200 (Sun, 03 Aug 2008) | 2 lines
Fix bug #2011470: "DRASCULA: slowdown when you hold mouse buttons"
........
r33578 | athrxx | 2008-08-03 14:09:48 +0200 (Sun, 03 Aug 2008) | 1 line
add support for HOF Italian fan translation to kyra.dat
........
r33579 | lordhoto | 2008-08-03 14:16:53 +0200 (Sun, 03 Aug 2008) | 2 lines
Added detection entries for kyra2 cd italian fan translation (see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"). (totally untested!)
........
r33580 | athrxx | 2008-08-03 14:50:56 +0200 (Sun, 03 Aug 2008) | 1 line
fix last commit (invalid kyra.dat game features for HOFCD)
........
r33581 | lordhoto | 2008-08-03 14:53:12 +0200 (Sun, 03 Aug 2008) | 2 lines
Removed comment.
........
r33584 | fingolfin | 2008-08-03 18:54:18 +0200 (Sun, 03 Aug 2008) | 1 line
New OSystem API for loading/storing default config file
........
r33585 | fingolfin | 2008-08-03 19:05:01 +0200 (Sun, 03 Aug 2008) | 1 line
OSYSTEM: Pushed some SDL/Symbian specific code to the respective backends; made openConfigFileForReading/openConfigFileForWriting return 0 if they failed to open a file
........
r33586 | fingolfin | 2008-08-03 20:11:27 +0200 (Sun, 03 Aug 2008) | 1 line
Implemented DumpFile::open(FSNode)
........
r33587 | fingolfin | 2008-08-03 20:29:37 +0200 (Sun, 03 Aug 2008) | 1 line
cleanup
........
r33590 | fingolfin | 2008-08-03 20:35:51 +0200 (Sun, 03 Aug 2008) | 1 line
FilesystemNode code: some comment cleanup; added FilesystemNode::openForReading() and openForWriting() methods (for now these are simple wrappers around Common::File)
........
r33596 | Kirben | 2008-08-04 03:57:50 +0200 (Mon, 04 Aug 2008) | 1 line
Remove debug left over.
........
r33604 | fingolfin | 2008-08-04 13:30:47 +0200 (Mon, 04 Aug 2008) | 1 line
Moved the OutSaveFile::finalize method to WriteStream; implemented DumpFile::flush()
........
r33605 | fingolfin | 2008-08-04 13:32:42 +0200 (Mon, 04 Aug 2008) | 1 line
Removed join_paths from default save manager, and use FSNode API instead (may lead to regressions, watch out)
........
r33606 | fingolfin | 2008-08-04 13:38:25 +0200 (Mon, 04 Aug 2008) | 1 line
Turned InSaveFile & OutSaveFile into simple typedefs
........
r33607 | fingolfin | 2008-08-04 13:46:52 +0200 (Mon, 04 Aug 2008) | 1 line
SCUMM: Only try to load savefile metadata from slots >= 0
........
r33608 | fingolfin | 2008-08-04 13:48:33 +0200 (Mon, 04 Aug 2008) | 1 line
DefaultSaveFileManager: Killed class StdioSaveFile, used FilesystemNode::openForReading()/openForWriting() instead (may cause regressions, watch out)
........
r33609 | fingolfin | 2008-08-04 15:25:30 +0200 (Mon, 04 Aug 2008) | 1 line
Modified DumpFile::open to accept non-existing nodes, and to actually open files in write mode -- d'oh
........
r33610 | thebluegr | 2008-08-04 17:01:41 +0200 (Mon, 04 Aug 2008) | 1 line
Fixing compilation under MSVC
........
r33611 | thebluegr | 2008-08-04 17:23:08 +0200 (Mon, 04 Aug 2008) | 1 line
Added Italian translation for the load confirmation window in IHNM
........
r33614 | fingolfin | 2008-08-04 19:17:37 +0200 (Mon, 04 Aug 2008) | 1 line
Moved ConfMan.registerDefault calls for savepath to DefaultSaveFileManager (eventually, should be pushed into backends/platform/ code)
........
r33615 | fingolfin | 2008-08-04 19:41:07 +0200 (Mon, 04 Aug 2008) | 1 line
Modified DefaultSaveFileManager::checkPath to perform at least basic checks on non-UNIX, non-Symbian systems
........
r33617 | fingolfin | 2008-08-04 19:46:22 +0200 (Mon, 04 Aug 2008) | 1 line
Remove some code which was rendered obsolete by md5's commit r33610
........
r33618 | anotherguest | 2008-08-04 20:14:17 +0200 (Mon, 04 Aug 2008) | 3 lines
List the correct engines in about,
updated actions with Lure (from WinCE).
Increased min heap size to 5MB
........
r33620 | buddha_ | 2008-08-04 21:32:52 +0200 (Mon, 04 Aug 2008) | 7 lines
Fix for misplaced objects in mouse object selection (Operation Stealth specific).
- Implemented Operation Stealth specific part of getObjectUnderCursor
which handles negative frame values.
- Fixed a test case (Should test for ydif <= 0 although tested for ydif < 0).
- Made part-value be anded with 0x0F in a test case to comply with disassembly.
- Added comment about a test case which isn't present in the disassembly.
Removing it makes things crash sometimes so letting it be.
........
r33621 | fingolfin | 2008-08-04 21:38:28 +0200 (Mon, 04 Aug 2008) | 1 line
Pushed some port specific code from DefaultSaveFileManager out to port specific code in backends/platform/
........
r33623 | anotherguest | 2008-08-04 22:00:56 +0200 (Mon, 04 Aug 2008) | 1 line
Symbian build package should now contain drascula.dat also
........
r33626 | Kirben | 2008-08-05 02:21:46 +0200 (Tue, 05 Aug 2008) | 1 line
Fix win32 compile.
........
r33627 | Kirben | 2008-08-05 02:27:25 +0200 (Tue, 05 Aug 2008) | 1 line
Removed unneeded #includes.
........
r33632 | lordhoto | 2008-08-05 13:32:16 +0200 (Tue, 05 Aug 2008) | 2 lines
Changed implementation parameters/return values to match definition.
........
r33633 | Kirben | 2008-08-05 13:42:04 +0200 (Tue, 05 Aug 2008) | 1 line
Remove file attribute check, since getChild() can be called on file path that doesn'r exist.
........
r33635 | lordhoto | 2008-08-05 14:13:01 +0200 (Tue, 05 Aug 2008) | 2 lines
Output errors on (some) missing files.
........
r33637 | buddha_ | 2008-08-05 14:58:23 +0200 (Tue, 05 Aug 2008) | 7 lines
Fix prompt updating regression introduced in r32257:
Previously prompt didn't get updated all the time,
like e.g. when selecting EXAMINE and moving the
cursor over to DOOR and then moving the cursor to
a place where there was no selectable object. The
prompt would've still shown "EXAMINE DOOR", now
it shows just "EXAMINE" which is correct AFAIK.
........
r33639 | fingolfin | 2008-08-05 15:08:07 +0200 (Tue, 05 Aug 2008) | 1 line
SCUMM: Don't try to load metadata for slot -1 (i.e. don't try to load metadata if nothing is selected)
........
r33640 | fingolfin | 2008-08-05 15:11:22 +0200 (Tue, 05 Aug 2008) | 1 line
SCUMM: cleanup SaveLoadChooser::updateInfos code
........
r33642 | buddha_ | 2008-08-05 20:17:55 +0200 (Tue, 05 Aug 2008) | 1 line
Add detection entry for a previously unrecognized 16 color PC version of Operation Stealth (The John Glames variant).
........
r33643 | buddha_ | 2008-08-05 21:30:16 +0200 (Tue, 05 Aug 2008) | 5 lines
Made rest of resource loading functions used in loadResource return -1 on error.
This fixed a crash in Operation Stealth when walking out of the airport
(Some file couldn't be opened and the game crashed because of that.
Now it doesn't crash but handles the missing file gracefully).
........
r33644 | buddha_ | 2008-08-05 23:20:11 +0200 (Tue, 05 Aug 2008) | 1 line
Workaround for missing player character animation when exiting the airport in Santa Paragua in at least the 256 color PC version of Operation Stealth.
........
r33645 | fingolfin | 2008-08-05 23:26:04 +0200 (Tue, 05 Aug 2008) | 1 line
Patch #2014663: Do not mark stack as executable in HQx scaler
........
r33648 | eriktorbjorn | 2008-08-05 23:43:10 +0200 (Tue, 05 Aug 2008) | 4 lines
Committed my patch #2026097 ("ALSA: Try both 65:0 and 17:0 by default"), with a
slight modification to the README changes. (I don't know how to interpret all
the output from aconnect, so I'm only documenting "the most important bit".)
........
r33654 | fingolfin | 2008-08-06 11:13:57 +0200 (Wed, 06 Aug 2008) | 1 line
SCUMM: Renamed inventoryScript -> inventoryScriptIndy3Mac
........
r33655 | fingolfin | 2008-08-06 12:32:20 +0200 (Wed, 06 Aug 2008) | 1 line
Workaround for bug #2016521 (DOTT: Bernard impersonating LaVerne)
........
r33664 | fingolfin | 2008-08-06 18:03:06 +0200 (Wed, 06 Aug 2008) | 1 line
Do not query savestates which obviously do not exist (to shut up bogus console warnings)
........
r33667 | buddha_ | 2008-08-06 22:41:53 +0200 (Wed, 06 Aug 2008) | 5 lines
HACK: Force oxygen to maximum during Operation Stealth's first arcade sequence.
This way the arcade sequence is completable now.
This hack should be removed later and the first
arcade sequence be made properly playable!
........
r33668 | buddha_ | 2008-08-06 22:47:59 +0200 (Wed, 06 Aug 2008) | 5 lines
Add 320x200 screen bounds checking to Operation Stealth's checkCollision function.
Fixes teleporting bug when getting out of the water after first arcade sequence.
The player previously got teleported to the beach near the hotel. Now it doesn't happen.
The player is still caught motionless and can't move after this fix though...
........
r33669 | buddha_ | 2008-08-06 22:54:38 +0200 (Wed, 06 Aug 2008) | 1 line
Renamed page3Raw to collisionPage so it's more apparent what it does.
........
r33670 | buddha_ | 2008-08-06 22:58:02 +0200 (Wed, 06 Aug 2008) | 1 line
Tiny comment update to loadCt-opcodes: Load collision table data.
........
r33673 | buddha_ | 2008-08-07 01:12:25 +0200 (Thu, 07 Aug 2008) | 4 lines
HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck.
The player's position is changed only by *one* pixel but it helps
and makes it possible to carry on with the game (Previously the player was totally stuck).
........
r33674 | knakos | 2008-08-07 11:10:42 +0200 (Thu, 07 Aug 2008) | 1 line
smartphone-specific code not needed anymore
........
r33678 | eriktorbjorn | 2008-08-07 15:36:02 +0200 (Thu, 07 Aug 2008) | 3 lines
Initialise the _sound object. Otherwise, ScummVM crashes for me if I use a native
MIDI driver instead of Adlib.
........
r33682 | knakos | 2008-08-07 20:36:12 +0200 (Thu, 07 Aug 2008) | 1 line
setup interface for later kyra games
........
r33686 | buddha_ | 2008-08-07 21:04:19 +0200 (Thu, 07 Aug 2008) | 7 lines
Fix for popup boxes sometimes blocking animation when they shouldn't in Operation Stealth:
- Made waitForPlayerClick updating more like in the original.
- Moved removeMessages to after the frame drawing in main loop hoping to be more like the original.
- Added an additional test to Operation Stealth's implementation of overlay type 2 drawing.
- Added an additional parameter incrementing and testing to Operation Stealth's removeMessages.
Hopefully this won't cause any regressions in Future Wars!
........
r33687 | buddha_ | 2008-08-07 21:31:12 +0200 (Thu, 07 Aug 2008) | 1 line
Made drawPlainBox handle border cases so it won't corrupt memory so easily. This may help with some memory corruption issues when for an example trying to draw the player's command string out of screen.
........
r33688 | buddha_ | 2008-08-07 21:46:06 +0200 (Thu, 07 Aug 2008) | 1 line
Added precautionary tests to background scrolling function in Operation Stealth to avoid possible memory corruptions.
........
r33689 | buddha_ | 2008-08-07 23:46:56 +0200 (Thu, 07 Aug 2008) | 1 line
Implemented game speed changing by pressing - or + to e.g. ease testing.
........
r33690 | cyx | 2008-08-07 23:49:55 +0200 (Thu, 07 Aug 2008) | 1 line
fix for bug #2040311: TOUCHE: Missing characters in the spanish version
........
r33691 | cyx | 2008-08-07 23:50:12 +0200 (Thu, 07 Aug 2008) | 1 line
fix for bug #2040484: TOUCHE: Graphic glitch with long answer options
........
r33693 | Kirben | 2008-08-08 04:18:17 +0200 (Fri, 08 Aug 2008) | 1 line
Hopefully allow quiting at any stage AGOS engines games again.
........
r33696 | Kirben | 2008-08-08 04:49:51 +0200 (Fri, 08 Aug 2008) | 1 line
Remove debug leftover.
........
r33698 | buddha_ | 2008-08-08 15:30:01 +0200 (Fri, 08 Aug 2008) | 9 lines
Added basic moving using keyboard (Only works in Operation Stealth at the moment):
- Should make it possible to complete the first arcade sequence legitimately
Renamed input variables (Their names were mixed up between Future Wars and Operation Stealth):
- Future Wars's inputVar1 -> egoMovedWithKeyboard
- Future Wars's inputVar2 -> xMoveKeyb
- Future Wars's inputVar3 -> yMoveKeyb
- Operation Stealth's inputVar0 -> xMoveKeyb
- Operation Stealth's inputVar1 -> yMoveKeyb
........
r33700 | buddha_ | 2008-08-08 16:46:19 +0200 (Fri, 08 Aug 2008) | 1 line
Partially fix Adlib volume setting in Cine (Now uses music volume for Adlib volume, previously always played with full volume. Doesn't differentiate between playing sound effects and music!).
........
r33703 | buddha_ | 2008-08-08 20:28:13 +0200 (Fri, 08 Aug 2008) | 4 lines
Added detection entry for the Sony published CD version of Future Wars.
Also added decrypting of the crypted AUTO00.PRC for this particular version.
Now this version should work but no CD audio support for it yet though.
........
r33708 | buddha_ | 2008-08-09 12:44:12 +0200 (Sat, 09 Aug 2008) | 1 line
Small cleanup: Renamed rol to rolByte and made it handle all cases.
........
r33710 | buddha_ | 2008-08-09 12:52:48 +0200 (Sat, 09 Aug 2008) | 1 line
Removing moving with keys qweasdzxc for now because they messed with savegame name typing. One can move with the cursor keys anyway.
........
r33712 | buddha_ | 2008-08-09 17:17:32 +0200 (Sat, 09 Aug 2008) | 1 line
Added updating of global variable var5 to Operation Stealth's renderOverlay function's type 20 overlay case (Previously var5 wasn't updated anywhere!). Also added a lower bound for var5's value into a comparison (Previously only the upper bound was tested for).
........
r33713 | buddha_ | 2008-08-09 18:07:08 +0200 (Sat, 09 Aug 2008) | 1 line
Added preliminary version of drawing type 22 overlays (Such overlays are added with opcode 0xA4 (o2_loadMask22) and removed with opcode 0xA5 (o2_unloadMask22)). Things might be wrong so needs testing! WIP!
........
r33714 | knakos | 2008-08-09 20:09:24 +0200 (Sat, 09 Aug 2008) | 1 line
fix MR on ce: allocate large buffer on the heap instead and of the stack, and improve the workaround
........
r33716 | knakos | 2008-08-09 20:12:43 +0200 (Sat, 09 Aug 2008) | 1 line
hotfix around drascula mem leak problems. see also bug report
........
r33719 | dhewg | 2008-08-09 20:37:18 +0200 (Sat, 09 Aug 2008) | 1 line
Updates to the recent OSystem API changes
........
r33721 | buddha_ | 2008-08-09 21:47:05 +0200 (Sat, 09 Aug 2008) | 7 lines
Made drawPlainBox's effective width and height always at least one.
- Makes oxygen gauge during first arcade sequence visible as it gave drawPlainBox a height of zero.
Made type 21 overlays be drawn as type 22 (Filled rectangle).
- For an example the oxygen gauge during the first arcade sequence is a type 21 overlay.
Flipped type 21 & 22 overlays as it looks correct for the oxygen gauge.
These features are quite WIP, they need testing to see if they're correct.
........
r33722 | buddha_ | 2008-08-09 21:57:46 +0200 (Sat, 09 Aug 2008) | 1 line
Made type 21 & 22 overlays not be flipped (The oxygen gauge looks actually better when it reaches zero this way although in its fully charged state it still does look a bit off. Disassembly seems to agree with not flipping the overlays so I'm going with this one for now).
........
r33723 | dhewg | 2008-08-09 22:02:56 +0200 (Sat, 09 Aug 2008) | 3 lines
Updated the Makefile to the new build system.
Introduced a Makefile variable to build a gamecube version.
........
r33725 | buddha_ | 2008-08-09 22:50:10 +0200 (Sat, 09 Aug 2008) | 1 line
Converted objectTable from a plain array to a Common::Array. Should help to catch out of bounds access errors that may cause memory corruption.
........
r33726 | buddha_ | 2008-08-09 22:55:01 +0200 (Sat, 09 Aug 2008) | 1 line
Converted animDataTable from a plain array to a Common::Array. Should help to catch out of bounds access errors which may cause memory corruption.
........
r33727 | buddha_ | 2008-08-10 00:38:03 +0200 (Sun, 10 Aug 2008) | 1 line
Converted zoneData and zoneQuery tables from plain array types to Common::Array. Should help catch out of bounds access errors that may cause memory corruption.
........
r33729 | fingolfin | 2008-08-10 00:42:03 +0200 (Sun, 10 Aug 2008) | 1 line
If no default config file is present, just don't load it, instead of crashing
........
r33730 | fingolfin | 2008-08-10 01:07:32 +0200 (Sun, 10 Aug 2008) | 1 line
Use FSNode API in default OSystem::openConfigFileForReading & OSystem::openConfigFileForWriting method implementations
........
r33733 | lordhoto | 2008-08-10 02:17:56 +0200 (Sun, 10 Aug 2008) | 2 lines
Fixed crash in KyraEngine_HoF::pauseEngineIntern.
........
r33736 | lordhoto | 2008-08-10 02:36:46 +0200 (Sun, 10 Aug 2008) | 2 lines
Fixed kyra to support properly support restarting of engine (for GSoC RTL, based on a patch from Chris Page).
........
r33742 | lordhoto | 2008-08-10 03:28:00 +0200 (Sun, 10 Aug 2008) | 2 lines
Added fallback to text only mode when voice files are not found.
........
r33754 | knakos | 2008-08-10 19:10:09 +0200 (Sun, 10 Aug 2008) | 1 line
add support for drascula engine
........
r33756 | knakos | 2008-08-10 19:13:24 +0200 (Sun, 10 Aug 2008) | 1 line
hint the backend to show the VK
........
r33758 | knakos | 2008-08-10 19:15:30 +0200 (Sun, 10 Aug 2008) | 1 line
address the ignored events and bad inventory screen handling 'features'
........
r33761 | sev | 2008-08-10 19:35:42 +0200 (Sun, 10 Aug 2008) | 2 lines
Add checksums for ScummVM repacked Italian and Spanish versions.
........
r33763 | eriktorbjorn | 2008-08-10 19:59:42 +0200 (Sun, 10 Aug 2008) | 5 lines
Committed my patch #2040074 ("XMIDI callback control events"). At the moment, I'm
not aware of any game that actually uses this XMIDI feature, so its primary
function right now is to silence lots of warnings while running the DOS version
of Simon the Sorcerer 2.
........
r33766 | eriktorbjorn | 2008-08-10 20:44:00 +0200 (Sun, 10 Aug 2008) | 5 lines
Fixed a bunch of Valgrind warnings (mostly uninitialised variables, but also an
invalid free()), so that I can start and quit the game without any unexpected
warnings. (The obvious next step, of course, would be to actually *play* the game
for a bit...)
........
r33777 | athrxx | 2008-08-11 16:47:50 +0200 (Mon, 11 Aug 2008) | 2 lines
LOL: add support for floppy installer files
(startup will fail nonethelesse since we have different pak file names)
........
r33781 | buddha_ | 2008-08-11 22:09:03 +0200 (Mon, 11 Aug 2008) | 1 line
Changed readBundleFile to unpack data in-place and added debugging messages to the function.
........
r33782 | buddha_ | 2008-08-11 22:18:33 +0200 (Mon, 11 Aug 2008) | 1 line
Added a safeguard to readBundleFile so it shouldn't corrupt memory even if the input says the data's unpacked size is less than its packed size (This shouldn't ever happen with non-corrupted data).
........
r33783 | buddha_ | 2008-08-11 22:41:13 +0200 (Mon, 11 Aug 2008) | 1 line
Changed partBuffer from a pointer to a Common::Array. Removed numElementInPart variable as it's now equivalent with partBuffer.size().
........
r33784 | buddha_ | 2008-08-11 23:26:41 +0200 (Mon, 11 Aug 2008) | 1 line
Removed textDataPtr pointer as it's not used beyond the loadTextData function. Reworked loadTextData a bit so there are no two loops for the same thing (Also renamed some of the local variables).
........
r33785 | buddha_ | 2008-08-11 23:45:47 +0200 (Mon, 11 Aug 2008) | 1 line
Changed palPtr from a pointer to a Common::Array named palArray. Removed palEntriesCount variable as it's now equivalent to palArray.size().
........
r33786 | buddha_ | 2008-08-12 00:26:25 +0200 (Tue, 12 Aug 2008) | 1 line
Moved fontParamTable inside TextHandler struct and made it a constant size as that's what it is (No need for using malloc & free anymore). Previously we would've tried to free an array that wasn't heap-allocated in freePoldatDat (Freeing fontParamTable_standard or fontParamTable_alt), that's fixed.
........
r33787 | dhewg | 2008-08-12 00:43:00 +0200 (Tue, 12 Aug 2008) | 7 lines
video mode polishing:
* proper fullscreen video mode
* multiple graphic modes with different overscan values
* "fullscreen mode" stretches the picture on 16:9 displays
* fixed broken sword 1/2 gfx garbage
* support for setShakePos()
........
r33788 | dhewg | 2008-08-12 00:44:29 +0200 (Tue, 12 Aug 2008) | 1 line
build a wii binary per default
........
r33790 | buddha_ | 2008-08-12 01:01:32 +0200 (Tue, 12 Aug 2008) | 1 line
Implemented drawMessage changes for Operation Stealth's timed cutscenes (Negative colors are used for timed text boxes that are totally transparent, only the text is drawn).
........
r33792 | buddha_ | 2008-08-12 01:20:10 +0200 (Tue, 12 Aug 2008) | 1 line
Made Operation Stealth's action failure messages use a background color set by the opcode 0x49 'o1_setDefaultMenuBgColor'. Should fix the 'text hard to read' problems.
........
r33793 | buddha_ | 2008-08-12 02:13:27 +0200 (Tue, 12 Aug 2008) | 1 line
Changed commandBuffer from a char[80] to Common::String and made FWRenderer::setCommand use a Common::String. Hopefully this might help with the command buffer overflow stuff, although this isn't a fix for the problem behind it, just a bandaid.
........
r33795 | buddha_ | 2008-08-12 16:44:44 +0200 (Tue, 12 Aug 2008) | 1 line
Implemented Operation Stealth specific parts of processInventory and added another mouse button waiting loop into the function's end (It's in both Future Wars and Operation Stealth). Fixes inventory showing in Operation Stealth.
........
r33797 | aquadran | 2008-08-12 16:52:44 +0200 (Tue, 12 Aug 2008) | 1 line
added few ignores for build dirs
........
r33805 | buddha_ | 2008-08-12 21:33:17 +0200 (Tue, 12 Aug 2008) | 1 line
Implemented Operation Stealth's makeCommandLine.
........
r33807 | buddha_ | 2008-08-12 22:27:49 +0200 (Tue, 12 Aug 2008) | 1 line
Tiny comment fix.
........
r33808 | buddha_ | 2008-08-12 22:58:26 +0200 (Tue, 12 Aug 2008) | 1 line
Fix for GCC warning in OSRenderer::renderOverlay: declaration of 'len' shadows a previous local.
........
r33809 | joostp | 2008-08-12 23:23:40 +0200 (Tue, 12 Aug 2008) | 2 lines
fix typo 'baclup' -> 'backup'
........
r33810 | buddha_ | 2008-08-13 00:45:38 +0200 (Wed, 13 Aug 2008) | 1 line
Added possibility to get the read resource's size from readBundleFile. Made loadMsg handle input data that has empty strings residing just beyond the input buffer (Thanks Valgrind :-)).
........
r33811 | buddha_ | 2008-08-13 01:44:39 +0200 (Wed, 13 Aug 2008) | 1 line
Fixed drawPlainBox's boundary checking (It wrote outside the screen occasionally). Now using the Common::Rect for clipping, yay! It's good.
........
r33812 | buddha_ | 2008-08-13 01:56:13 +0200 (Wed, 13 Aug 2008) | 1 line
Hopefully fixes 'Conditional jump or move depends on uninitialised value(s)' Valgrind warning at sound.cpp:611.
........
r33815 | buddha_ | 2008-08-13 03:02:00 +0200 (Wed, 13 Aug 2008) | 1 line
Changed Agi::SoundMgr's sound buffer to a member array of size BUFFER_SIZE. Also added initialization of _playing to false in SoundMgr's constructor. Hopefully helps with the occasional crashes in the sound code when starting the first sound in an AGI game.
........
r33816 | buddha_ | 2008-08-13 03:10:03 +0200 (Wed, 13 Aug 2008) | 1 line
Shutting up Valgrind about using uninitialised values from array _chn in function stopNote.
........
r33817 | fingolfin | 2008-08-13 12:13:22 +0200 (Wed, 13 Aug 2008) | 1 line
Clarify SCUMM MD5 message: We do not want reports on fanmade translations
........
r33819 | buddha_ | 2008-08-13 13:36:45 +0200 (Wed, 13 Aug 2008) | 1 line
Reverted r33815 (But not r33816).
........
r33822 | buddha_ | 2008-08-13 13:57:48 +0200 (Wed, 13 Aug 2008) | 1 line
Fixed initialization of some SoundMgr-class's member variables. Moved _sndBuffer's allocation to SoundMgr's constructor and its deallocation to the destructor. Made fillAudio SoundMgr's method and removed a superfluous global static variable 'int16 *buffer'. Should help with the occasional crashes when starting the first sound in an AGI game.
........
r33826 | buddha_ | 2008-08-13 15:40:28 +0200 (Wed, 13 Aug 2008) | 1 line
Type 21 overlay comment update (Found the drawing routine in the disassembly and checked the original for how the oxygen gauge during the first arcade sequence looks like. They're some kind of sprites most likely and not just simply filled rectangles).
........
r33829 | knakos | 2008-08-13 17:16:38 +0200 (Wed, 13 Aug 2008) | 1 line
fix bug #2038992 by saving and restoring channel volumes
........
r33837 | lordhoto | 2008-08-13 21:10:31 +0200 (Wed, 13 Aug 2008) | 2 lines
Added detection entries for kyra3 Mac.
........
r33839 | knakos | 2008-08-13 21:13:53 +0200 (Wed, 13 Aug 2008) | 1 line
fix bug #1910057: FT multifunction
........
r33844 | knakos | 2008-08-13 21:32:25 +0200 (Wed, 13 Aug 2008) | 1 line
fix a small bug in the quit dialog
........
r33847 | fingolfin | 2008-08-13 22:37:54 +0200 (Wed, 13 Aug 2008) | 1 line
cleanup
........
r33858 | peres001 | 2008-08-14 09:10:42 +0200 (Thu, 14 Aug 2008) | 1 line
Removed cross-references between the general parser and the text formatter. To achieve this, I have rewritten the latter for both NS and BRA.
........
r33859 | peres001 | 2008-08-14 09:14:34 +0200 (Thu, 14 Aug 2008) | 1 line
Finally moved parseNextToken to Script, where it belongs!
........
r33860 | peres001 | 2008-08-14 12:04:43 +0200 (Thu, 14 Aug 2008) | 1 line
Cleanup.
........
r33861 | peres001 | 2008-08-14 12:24:39 +0200 (Thu, 14 Aug 2008) | 1 line
More cleanup.
........
r33864 | peres001 | 2008-08-14 15:45:32 +0200 (Thu, 14 Aug 2008) | 1 line
Some variable renamed to make more sense.
........
r33871 | buddha_ | 2008-08-14 22:18:13 +0200 (Thu, 14 Aug 2008) | 1 line
Added debug showing of the collision page when pressing the Alt key. Alt isn't used for anything else so one might as well use it for this.
........
r33872 | buddha_ | 2008-08-14 22:49:34 +0200 (Thu, 14 Aug 2008) | 1 line
Fix for bugging moving at the bottom of the ocean when trying to free the girl from the ropes and swimming to the surface. Some global variables related to mouse position weren't being updated in executePlayerInput, now they are and things seem to work. Also enables moving in the labyrinth arcade sequence at the palace.
........
r33873 | athrxx | 2008-08-14 23:21:04 +0200 (Thu, 14 Aug 2008) | 1 line
- HOF: another Italian fan translation
........
r33875 | athrxx | 2008-08-14 23:34:29 +0200 (Thu, 14 Aug 2008) | 1 line
- HOF: forgot french and german entries in last commit
........
r33877 | buddha_ | 2008-08-15 00:01:56 +0200 (Fri, 15 Aug 2008) | 1 line
Moved showing of the collision page from Alt-key to F11-key because Alt conflicted with taking screenshots using Alt-s. Great. Hopefully F11 doesn't conflict with anything useful.
........
r33878 | lordhoto | 2008-08-15 00:09:36 +0200 (Fri, 15 Aug 2008) | 2 lines
Improved version of my resource loading patch from -devel (check also r33876).
........
r33879 | lordhoto | 2008-08-15 00:11:56 +0200 (Fri, 15 Aug 2008) | 2 lines
Formatting.
........
r33886 | athrxx | 2008-08-15 01:46:37 +0200 (Fri, 15 Aug 2008) | 1 line
lol: new detection entry (german floppy extracted)
........
r33889 | peres001 | 2008-08-15 04:52:42 +0200 (Fri, 15 Aug 2008) | 2 lines
Simplified handling of script variables (especially locals).
........
r33890 | peres001 | 2008-08-15 05:36:01 +0200 (Fri, 15 Aug 2008) | 1 line
Disabled lip syncing code in BRA, since I couldn't find any scripts actually using it. Some warnings have been added as a sentinel.
........
r33891 | peres001 | 2008-08-15 05:59:45 +0200 (Fri, 15 Aug 2008) | 1 line
Made scripts access Animation fields via accessors and mutators, instead of using raw pointers.
........
r33892 | peres001 | 2008-08-15 06:30:45 +0200 (Fri, 15 Aug 2008) | 1 line
Made coordinates and frame number protected into Zone and Animation, and changed client code to use get/set. This will allow various simplifications (e.g. when calculating Z), and is a step towards correct handling of interactive zones of Zone/Animation in BRA.
........
r33894 | peres001 | 2008-08-15 10:44:41 +0200 (Fri, 15 Aug 2008) | 1 line
Updated layer calculation: animations are now hidden properly by background elements (but not items yet).
........
r33896 | drmccoy | 2008-08-15 12:47:42 +0200 (Fri, 15 Aug 2008) | 2 lines
Fixing a segfault in the Bargon Attack menu
........
r33900 | peres001 | 2008-08-15 16:25:43 +0200 (Fri, 15 Aug 2008) | 1 line
Added routines to load and handle item masks: animation are now correctly hidden when they are behind any object. The masks of items that are not explicitly activated (even if visible) still are handled (e.g. the crown in the museum is not visible in the foreground).
........
r33902 | anotherguest | 2008-08-15 16:38:25 +0200 (Fri, 15 Aug 2008) | 2 lines
UpdUpdated actions and Readme for Symbian OS port!
........
r33903 | peres001 | 2008-08-15 17:08:08 +0200 (Fri, 15 Aug 2008) | 1 line
Implemented raster operation for masks and postponed blitting of zones after everything in the location has been loaded. This fixes the remaining problems with animations not being masked by items.
........
r33919 | fingolfin | 2008-08-16 00:06:15 +0200 (Sat, 16 Aug 2008) | 1 line
SCUMM: Made some potentially ambiguous (to the reader, at least) checks slightly less ambiguous
........
svn-id: r33920
Diffstat (limited to 'engines')
124 files changed, 3207 insertions, 1508 deletions
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp index 77f79272f8..9fe8fbf41a 100644 --- a/engines/agi/sound.cpp +++ b/engines/agi/sound.cpp @@ -435,13 +435,10 @@ void IIgsMidiChannel::stopSounds() { _gsChannels.clear(); } -static int16 *buffer; - int SoundMgr::initSound() { int r = -1; - buffer = _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE); - + memset(_sndBuffer, 0, BUFFER_SIZE << 1); _env = false; switch (_vm->_soundemu) { @@ -478,7 +475,6 @@ int SoundMgr::initSound() { void SoundMgr::deinitSound() { debugC(3, kDebugLevelSound, "()"); _mixer->stopHandle(_soundHandle); - free(_sndBuffer); } void SoundMgr::stopNote(int i) { @@ -1185,7 +1181,7 @@ bool SoundMgr::loadInstruments() { return _gsSound.loadWaveFile(waveFsnode->getPath(), *exeInfo) && _gsSound.loadInstrumentHeaders(exeFsnode->getPath(), *exeInfo); } -static void fillAudio(void *udata, int16 *stream, uint len) { +void SoundMgr::fillAudio(void *udata, int16 *stream, uint len) { SoundMgr *soundMgr = (SoundMgr *)udata; uint32 p = 0; static uint32 n = 0, s = 0; @@ -1193,32 +1189,34 @@ static void fillAudio(void *udata, int16 *stream, uint len) { len <<= 2; debugC(5, kDebugLevelSound, "(%p, %p, %d)", (void *)udata, (void *)stream, len); - memcpy(stream, (uint8 *)buffer + s, p = n); + memcpy(stream, (uint8 *)_sndBuffer + s, p = n); for (n = 0, len -= p; n < len; p += n, len -= n) { soundMgr->playSound(); n = soundMgr->mixSound() << 1; if (len < n) { - memcpy((uint8 *)stream + p, buffer, len); + memcpy((uint8 *)stream + p, _sndBuffer, len); s = len; n -= s; return; } else { - memcpy((uint8 *)stream + p, buffer, n); + memcpy((uint8 *)stream + p, _sndBuffer, n); } } soundMgr->playSound(); n = soundMgr->mixSound() << 1; - memcpy((uint8 *)stream + p, buffer, s = len); + memcpy((uint8 *)stream + p, _sndBuffer, s = len); n -= s; } -SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) { +SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() { _vm = agi; _mixer = pMixer; _sampleRate = pMixer->getOutputRate(); _endflag = -1; _playingSound = -1; - _sndBuffer = 0; + _env = false; + _playing = false; + _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE); _waveform = 0; } @@ -1231,6 +1229,7 @@ void SoundMgr::setVolume(uint8 volume) { } SoundMgr::~SoundMgr() { + free(_sndBuffer); } } // End of namespace Agi diff --git a/engines/agi/sound.h b/engines/agi/sound.h index f1c2782421..a1f079891f 100644 --- a/engines/agi/sound.h +++ b/engines/agi/sound.h @@ -472,6 +472,7 @@ private: const int16 *_waveform; void premixerCall(int16 *buf, uint len); + void fillAudio(void *udata, int16 *stream, uint len); public: void unloadSound(int); diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index c92f834a3b..3e1b9b0611 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -151,7 +151,7 @@ void MoviePlayer::play() { startSound(); - while (_frameNum < _framesCount) + while (_frameNum < _framesCount && !_vm->_quit) handleNextFrame(); closeFile(); @@ -167,7 +167,7 @@ void MoviePlayer::play() { _vm->_system->setPalette(palette, 0, 256); } - _vm->fillBackGroundFromBack(); + _vm->fillBackGroundFromBack(); _vm->_fastFadeOutFlag = true; } diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp index d36549f187..6f4cd09947 100644 --- a/engines/agos/input.cpp +++ b/engines/agos/input.cpp @@ -123,7 +123,7 @@ void AGOSEngine::setup_cond_c_helper() { clearName(); _lastNameOn = last; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = 0; _leftButtonDown = 0; @@ -145,7 +145,7 @@ void AGOSEngine::setup_cond_c_helper() { } delay(100); - } while (_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0); + } while ((_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0) && !_quit); if (_lastHitArea == NULL) { } else if (_lastHitArea->id == 0x7FFB) { diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp index 3114b24549..891e9bde95 100644 --- a/engines/agos/midi.cpp +++ b/engines/agos/midi.cpp @@ -538,7 +538,11 @@ void MidiPlayer::loadXMIDI(Common::File *in, bool sfx) { error("Expected 'FORM' tag but found '%c%c%c%c' instead", buf[0], buf[1], buf[2], buf[3]); } - MidiParser *parser = MidiParser::createParser_XMIDI(); + // In the DOS version of Simon the Sorcerer 2, the music contains lots + // of XMIDI callback controller events. As far as we know, they aren't + // actually used, so we disable the callback handler explicitly. + + MidiParser *parser = MidiParser::createParser_XMIDI(NULL); parser->setMidiDriver(this); parser->setTimerRate(_driver->getBaseTempo()); if (!parser->loadMusic(p->data, size)) diff --git a/engines/agos/oracle.cpp b/engines/agos/oracle.cpp index a113c8e2ea..2d2feb7b9e 100644 --- a/engines/agos/oracle.cpp +++ b/engines/agos/oracle.cpp @@ -459,7 +459,7 @@ void AGOSEngine_Feeble::saveUserGame(int slot) { } windowPutChar(window, 0x7f); - for (;;) { + while (!_quit) { _keyPressed.reset(); delay(1); diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index 4a5c43e706..9779630d47 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -279,11 +279,11 @@ restart: name = buf; _saveGameNameLen = 0; - for (;;) { + while (!_quit) { windowPutChar(window, 128); _keyPressed.reset(); - for (;;) { + while (!_quit) { delay(10); if (_keyPressed.ascii && _keyPressed.ascii < 128) { i = _keyPressed.ascii; @@ -443,7 +443,7 @@ void AGOSEngine_Elvira2::userGame(bool load) { name = buf + 192; - for (;;) { + while (!_quit) { windowPutChar(window, 128); _saveLoadEdit = true; @@ -516,7 +516,7 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) { _keyPressed.reset(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; @@ -526,7 +526,7 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) { return _keyPressed.ascii; } delay(10); - } while (_lastHitArea3 == 0); + } while (_lastHitArea3 == 0 && !_quit); ha = _lastHitArea; if (ha == NULL || ha->id < 200) { @@ -543,6 +543,8 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) { return ha->id - 200; } } + + return 225; } void AGOSEngine_Simon1::listSaveGames(char *dst) { @@ -706,7 +708,7 @@ restart:; _saveGameNameLen++; } - for (;;) { + while (!_quit) { windowPutChar(window, 127); _saveLoadEdit = true; @@ -785,7 +787,7 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) { _keyPressed.reset(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; @@ -795,7 +797,7 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) { return _keyPressed.ascii; } delay(10); - } while (_lastHitArea3 == 0); + } while (_lastHitArea3 == 0 && !_quit); ha = _lastHitArea; if (ha == NULL || ha->id < 205) { @@ -824,6 +826,8 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) { return ha->id - 208; } } + + return 205; } void AGOSEngine::disableFileBoxes() { diff --git a/engines/agos/script.cpp b/engines/agos/script.cpp index 6758aec511..fa132ec26f 100644 --- a/engines/agos/script.cpp +++ b/engines/agos/script.cpp @@ -1012,7 +1012,7 @@ int AGOSEngine::runScript() { executeOpcode(_opcode); } while (getScriptCondition() != flag && !getScriptReturn() && !_quit); - return getScriptReturn(); + return (_quit) ? 1 : getScriptReturn(); } Child *nextSub(Child *sub, int16 key) { diff --git a/engines/agos/script_e1.cpp b/engines/agos/script_e1.cpp index c7e1d6736e..9b572e347b 100644 --- a/engines/agos/script_e1.cpp +++ b/engines/agos/script_e1.cpp @@ -1053,11 +1053,11 @@ uint AGOSEngine::confirmYesOrNo(uint16 x, uint16 y) { ha->priority = 999; ha->window = 0; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (_lastHitArea3 != 0) break; delay(1); @@ -1102,11 +1102,11 @@ uint AGOSEngine::continueOrQuit() { ha->priority = 999; ha->window = 0; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (_lastHitArea3 != 0) break; delay(1); diff --git a/engines/agos/script_e2.cpp b/engines/agos/script_e2.cpp index da9afc5a7d..6f6db8efb4 100644 --- a/engines/agos/script_e2.cpp +++ b/engines/agos/script_e2.cpp @@ -370,11 +370,11 @@ void AGOSEngine_Elvira2::oe2_pauseGame() { uint32 pauseTime = getTime(); haltAnimation(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (processSpecialKeys() != 0 || _lastHitArea3 != 0) break; delay(1); diff --git a/engines/agos/script_s1.cpp b/engines/agos/script_s1.cpp index 51918b9515..6183a3fb20 100644 --- a/engines/agos/script_s1.cpp +++ b/engines/agos/script_s1.cpp @@ -339,18 +339,8 @@ void AGOSEngine_Simon1::os1_pauseGame() { break; } - for (;;) { + while (!_quit) { delay(1); -#ifdef _WIN32_WCE - if (isSmartphone()) { - if (_keyPressed.keycode) { - if (_keyPressed.keycode == Common::KEYCODE_RETURN) - _quit = true; - else - break; - } - } -#endif if (_keyPressed.keycode == keyYes) _quit = true; else if (_keyPressed.keycode == keyNo) diff --git a/engines/agos/script_ww.cpp b/engines/agos/script_ww.cpp index 5fd83312c3..8dc915f6e8 100644 --- a/engines/agos/script_ww.cpp +++ b/engines/agos/script_ww.cpp @@ -368,11 +368,11 @@ void AGOSEngine_Waxworks::oww_pauseGame() { uint32 pauseTime = getTime(); haltAnimation(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (_lastHitArea3 != 0) break; delay(1); diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp index 963bd6bd86..c8f6d40f1f 100644 --- a/engines/agos/verb.cpp +++ b/engines/agos/verb.cpp @@ -343,6 +343,9 @@ void AGOSEngine::handleVerbClicked(uint verb) { Subroutine *sub; int result; + if (_quit) + return; + _objectItem = _hitAreaObjectItem; if (_objectItem == _dummyItem2) { _objectItem = me(); diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp index e25bd6b438..e1f986b92e 100644 --- a/engines/agos/window.cpp +++ b/engines/agos/window.cpp @@ -298,7 +298,7 @@ void AGOSEngine::waitWindow(WindowBlock *window) { ha->id = 0x7FFF; ha->priority = 999; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index 8dbccebedf..a670328c12 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -49,7 +49,7 @@ struct AnimHeader2Struct { uint16 field_E; }; -AnimData animDataTable[NUM_MAX_ANIMDATA]; +Common::Array<AnimData> animDataTable; static const AnimDataEntry transparencyData[] = { {"ALPHA", 0xF}, @@ -511,14 +511,15 @@ int emptyAnimSpace(int start = 0) { /*! \brief Load SPL data into animDataTable * \param resourceName SPL filename - * \param idx Target index in animDataTable + * \param idx Target index in animDataTable (-1 if any empty space will do) + * \return The number of the animDataTable entry after the loaded SPL data (-1 if error) */ -void loadSpl(const char *resourceName, int16 idx) { +int loadSpl(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); int entry; if (foundFileIdx < 0) { - return; + return -1; } byte *dataPtr = readBundleFile(foundFileIdx); @@ -528,13 +529,20 @@ void loadSpl(const char *resourceName, int16 idx) { animDataTable[entry].load(dataPtr, ANIM_RAW, partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName); free(dataPtr); + return entry + 1; } /*! \brief Load 1bpp mask * \param resourceName Mask filename + * \param idx Target index in animDataTable (-1 if any empty space will do) + * \return The number of the animDataTable entry after the loaded mask (-1 if error) */ -void loadMsk(const char *resourceName) { +int loadMsk(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + int entry = 0; byte *dataPtr = readBundleFile(foundFileIdx); byte *ptr; @@ -544,21 +552,28 @@ void loadMsk(const char *resourceName) { loadAnimHeader(animHeader, readS); ptr = dataPtr + 0x16; + entry = idx < 0 ? emptyAnimSpace() : idx; + assert(entry >= 0); for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { - entry = emptyAnimSpace(entry); - assert(entry >= 0); animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName); ptr += animHeader.frameWidth * animHeader.frameHeight; } free(dataPtr); + return entry; } /*! \brief Load animation * \param resourceName Animation filename + * \param idx Target index in animDataTable (-1 if any empty space will do) + * \return The number of the animDataTable entry after the loaded animation (-1 if error) */ -void loadAni(const char *resourceName) { +int loadAni(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + int entry = 0; byte *dataPtr = readBundleFile(foundFileIdx); byte *ptr; @@ -571,10 +586,10 @@ void loadAni(const char *resourceName) { transparentColor = getAnimTransparentColor(resourceName); - for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { - entry = emptyAnimSpace(entry); - assert(entry >= 0); + entry = idx < 0 ? emptyAnimSpace() : idx; + assert(entry >= 0); + for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { // special case transparency handling if (!strcmp(resourceName, "L2202.ANI")) { transparentColor = i < 2 ? 0 : 7; @@ -587,6 +602,7 @@ void loadAni(const char *resourceName) { } free(dataPtr); + return entry; } /*! \brief Decode 16 color image with palette @@ -642,16 +658,21 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { /*! \brief Load image set * \param resourceName Image set filename - * \param idx Target index in animDataTable + * \param idx Target index in animDataTable (-1 if any empty space will do) + * \return The number of the animDataTable entry after the loaded image set (-1 if error) */ -void loadSet(const char *resourceName, int16 idx) { +int loadSet(const char *resourceName, int16 idx) { AnimHeader2Struct header2; uint16 numSpriteInAnim; int16 foundFileIdx = findFileInBundle(resourceName); - int16 entry = idx >= 0 ? idx : 0; + int16 entry; byte *ptr, *startOfDataPtr, *dataPtr, *origDataPtr; int type; + if (foundFileIdx < 0) { + return -1; + } + origDataPtr = dataPtr = readBundleFile(foundFileIdx); assert(!memcmp(dataPtr, "SET", 3)); ptr = dataPtr + 4; @@ -661,6 +682,9 @@ void loadSet(const char *resourceName, int16 idx) { startOfDataPtr = ptr + numSpriteInAnim * 0x10; + entry = idx < 0 ? emptyAnimSpace() : idx; + assert(entry >= 0); + for (int16 i = 0; i < numSpriteInAnim; i++, entry++) { Common::MemoryReadStream readS(ptr, 0x10); @@ -674,9 +698,6 @@ void loadSet(const char *resourceName, int16 idx) { ptr += 0x10; - entry = idx < 0 ? emptyAnimSpace(entry) : idx + i; - assert(entry >= 0); - dataPtr = startOfDataPtr + header2.field_0; if (header2.type == 1) { @@ -693,78 +714,59 @@ void loadSet(const char *resourceName, int16 idx) { } free(origDataPtr); + return entry; } /*! \brief Load SEQ data into animDataTable * \param resourceName SEQ data filename - * \param idx Target index in animDataTable + * \param idx Target index in animDataTable (-1 if any empty space will do) + * \return The number of the animDataTable entry after the loaded SEQ data (-1 if error) */ -void loadSeq(const char *resourceName, int16 idx) { +int loadSeq(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + byte *dataPtr = readBundleFile(foundFileIdx); int entry = idx < 0 ? emptyAnimSpace() : idx; animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName); free(dataPtr); + return entry + 1; } -void loadResource(const char *resourceName) { - /* byte isMask = 0; */ - /* byte isSpl = 0; */ - +/*! \brief Load a resource into animDataTable + * \param resourceName Resource's filename + * \param idx Target index in animDataTable (-1 if any empty space will do) + * \return The number of the animDataTable entry after the loaded resource (-1 if error) + * \todo Implement loading of all resource types + */ +int loadResource(const char *resourceName, int16 idx) { + int result = -1; // Return an error by default if (strstr(resourceName, ".SPL")) { - loadSpl(resourceName, -1); - return; + result = loadSpl(resourceName, idx); } else if (strstr(resourceName, ".MSK")) { - loadMsk(resourceName); - return; + result = loadMsk(resourceName, idx); } else if (strstr(resourceName, ".ANI")) { - loadAni(resourceName); - return; + result = loadAni(resourceName, idx); } else if (strstr(resourceName, ".ANM")) { - loadAni(resourceName); - return; + result = loadAni(resourceName, idx); } else if (strstr(resourceName, ".SET")) { - loadSet(resourceName, -1); - return; + result = loadSet(resourceName, idx); } else if (strstr(resourceName, ".SEQ")) { - loadSeq(resourceName, -1); - return; - } else if (strstr(resourceName, "ECHEC")) { // Echec (French) means failure - exitEngine = 1; - return; - } - - error("loadResource: Cannot determine type for '%s'", resourceName); -} - -/*! \todo There seems to be some additional resource file that is not loaded - */ -void loadAbs(const char *resourceName, uint16 idx) { - /* byte isMask = 0; */ - /* byte isSpl = 0; */ - - if (strstr(resourceName, ".SET")) { - loadSet(resourceName, idx); - return; + result = loadSeq(resourceName, idx); } else if (strstr(resourceName, ".H32")) { - warning("Ignoring file %s (load at %d)", resourceName, idx); - return; - } else if (strstr(resourceName, ".SEQ")) { - loadSeq(resourceName, idx); - return; - } else if (strstr(resourceName, ".SPL")) { - loadSpl(resourceName, idx); - return; + warning("loadResource: Ignoring file '%s' (Load at %d)", resourceName, idx); } else if (strstr(resourceName, ".AMI")) { - warning("Ignoring file %s (load at %d)", resourceName, idx); - return; - } else if (strstr(resourceName, ".ANI")) { - warning("Ignoring file %s (load at %d)", resourceName, idx); - return; + warning("loadResource: Ignoring file '%s' (Load at %d)", resourceName, idx); + } else if (strstr(resourceName, "ECHEC")) { // Echec (French) means failure + exitEngine = 1; + } else { + error("loadResource: Cannot determine type for '%s'", resourceName); } - error("loadAbs: Cannot determine type for '%s'", resourceName); + return result; } /*! \brief Load animDataTable from save @@ -776,17 +778,9 @@ void loadAbs(const char *resourceName, uint16 idx) { * at a time. */ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) { - int16 currentAnim, foundFileIdx; - int8 isMask = 0, isSpl = 0; - byte *dataPtr, *ptr; - char *animName, part[256]; - byte transparentColor = 0; - AnimHeaderStruct animHeader; - + int16 currentAnim, foundFileIdx, frame; + char *animName, part[256], name[10]; uint16 width, height, bpp, var1; - int16 frame; - char name[10]; - int type; strcpy(part, currentPartName); @@ -795,11 +789,8 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); const int fileStartPos = fHandle.pos(); - for (currentAnim = 0; currentAnim < NUM_MAX_ANIMDATA; currentAnim += animHeader.numFrames) { - // Initialize the number of frames variable to a sane number. - // This is needed when using continue later in this function. - animHeader.numFrames = 1; - + currentAnim = 0; + while (currentAnim < NUM_MAX_ANIMDATA) { // Seek to the start of the current animation's entry fHandle.seek(fileStartPos + currentAnim * entrySize); // Read in the current animation entry @@ -826,6 +817,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam // Don't try to load invalid entries. if (foundFileIdx < 0 || !validPtr) { + currentAnim++; // Jump over the invalid entry continue; } @@ -836,52 +828,10 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam } animName = partBuffer[foundFileIdx].partName; - ptr = dataPtr = readBundleFile(foundFileIdx); - - // isSpl and isMask are mutually exclusive cases - isSpl = (strstr(animName, ".SPL")) ? 1 : 0; - isMask = (strstr(animName, ".MSK")) ? 1 : 0; - - if (isSpl) { - width = (uint16) partBuffer[foundFileIdx].unpackedSize; - height = 1; - animHeader.numFrames = 1; - type = ANIM_RAW; - } else { - Common::MemoryReadStream readS(ptr, 0x16); - loadAnimHeader(animHeader, readS); - ptr += 0x16; - - width = animHeader.frameWidth; - height = animHeader.frameHeight; - - if (isMask) { - type = ANIM_MASK; - } else { - type = ANIM_MASKSPRITE; - } - } - - loadRelatedPalette(animName); - transparentColor = getAnimTransparentColor(animName); - // Make sure we load at least one frame and also that we - // don't overflow the animDataTable by writing beyond its end. - animHeader.numFrames = CLIP<uint16>(animHeader.numFrames, 1, NUM_MAX_ANIMDATA - currentAnim); - - // Load the frames - for (frame = 0; frame < animHeader.numFrames; frame++) { - // special case transparency handling - if (!strcmp(animName, "L2202.ANI")) { - transparentColor = (frame < 2) ? 0 : 7; - } else if (!strcmp(animName, "L4601.ANI")) { - transparentColor = (frame < 1) ? 0xE : 0; - } - - // Load a single frame - animDataTable[currentAnim + frame].load(ptr + frame * width * height, type, width, height, foundFileIdx, frame, name, transparentColor); - } - - free(dataPtr); + loadRelatedPalette(animName); // Is this for Future Wars only? + const int16 prevAnim = currentAnim; + currentAnim = loadResource(animName, currentAnim); + assert(currentAnim > prevAnim); // Make sure we advance forward } loadPart(part); diff --git a/engines/cine/anim.h b/engines/cine/anim.h index b0ce55f7ee..8b1541eb3f 100644 --- a/engines/cine/anim.h +++ b/engines/cine/anim.h @@ -150,12 +150,11 @@ public: #define NUM_MAX_ANIMDATA 255 -extern AnimData animDataTable[NUM_MAX_ANIMDATA]; +extern Common::Array<AnimData> animDataTable; void freeAnimDataTable(void); void freeAnimDataRange(byte startIdx, byte numIdx); -void loadResource(const char *resourceName); -void loadAbs(const char *resourceName, uint16 idx); +int loadResource(const char *resourceName, int16 idx = -1); void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat); void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency); diff --git a/engines/cine/bg.cpp b/engines/cine/bg.cpp index 2a4e7f0ab1..45bfae7925 100644 --- a/engines/cine/bg.cpp +++ b/engines/cine/bg.cpp @@ -56,7 +56,7 @@ byte loadCtFW(const char *ctName) { header[i] = readS.readUint16BE(); } - gfxConvertSpriteToRaw(page3Raw, ptr + 0x80, 160, 200); + gfxConvertSpriteToRaw(collisionPage, ptr + 0x80, 160, 200); free(dataPtr); return 0; @@ -74,10 +74,10 @@ byte loadCtOS(const char *ctName) { ptr += 2; if (bpp == 8) { - memcpy(page3Raw, ptr + 256 * 3, 320 * 200); + memcpy(collisionPage, ptr + 256 * 3, 320 * 200); renderer->loadCt256(ptr, ctName); } else { - gfxConvertSpriteToRaw(page3Raw, ptr + 32, 160, 200); + gfxConvertSpriteToRaw(collisionPage, ptr + 32, 160, 200); renderer->loadCt16(ptr, ctName); } diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index f6778b6457..9eb751835e 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -57,6 +57,10 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng // Setup mixer _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + // Use music volume for plain sound types (At least the Adlib player uses a plain sound type + // so previously the music and sfx volume controls didn't affect it at all). + // FIXME: Make Adlib player differentiate between playing sound effects and music and remove this. + _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("music_volume")); g_cine = this; @@ -65,14 +69,9 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng CineEngine::~CineEngine() { if (g_cine->getGameType() == Cine::GType_OS) { - freePoldatDat(); freeErrmessDat(); } Common::clearAllSpecialDebugLevels(); - - free(palPtr); - free(partBuffer); - free(textDataPtr); } int CineEngine::init() { @@ -100,13 +99,43 @@ int CineEngine::go() { mainLoop(1); delete renderer; - delete[] page3Raw; + delete[] collisionPage; delete g_sound; return 0; } +int CineEngine::getTimerDelay() const { + return (10923000 * _timerDelayMultiplier) / 1193180; +} + +/*! \brief Modify game speed + * \param speedChange Negative values slow game down, positive values speed it up, zero does nothing + * \return Timer delay multiplier's value after the game speed change + */ +int CineEngine::modifyGameSpeed(int speedChange) { + // If we want more speed we decrement the timer delay multiplier and vice versa. + _timerDelayMultiplier = CLIP(_timerDelayMultiplier - speedChange, 1, 50); + return _timerDelayMultiplier; +} void CineEngine::initialize() { + // Resize object table to its correct size and reset all its elements + objectTable.resize(NUM_MAX_OBJECT); + resetObjectTable(); + + // Resize animation data table to its correct size and reset all its elements + animDataTable.resize(NUM_MAX_ANIMDATA); + freeAnimDataTable(); + + // Resize zone data table to its correct size and reset all its elements + zoneData.resize(NUM_MAX_ZONE); + Common::set_to(zoneData.begin(), zoneData.end(), 0); + + // Resize zone query table to its correct size and reset all its elements + zoneQuery.resize(NUM_MAX_ZONE); + Common::set_to(zoneQuery.begin(), zoneQuery.end(), 0); + + _timerDelayMultiplier = 12; // Set default speed setupOpcodes(); initLanguage(g_cine->getLanguage()); @@ -117,16 +146,17 @@ void CineEngine::initialize() { renderer = new FWRenderer; } - page3Raw = new byte[320 * 200]; - textDataPtr = (byte *)malloc(8000); + collisionPage = new byte[320 * 200]; - partBuffer = (PartBuffer *)malloc(NUM_MAX_PARTDATA * sizeof(PartBuffer)); + // Clear part buffer as there's nothing loaded into it yet. + // Its size will change when loading data into it with the loadPart function. + partBuffer.clear(); if (g_cine->getGameType() == Cine::GType_OS) { readVolCnf(); } - loadTextData("texte.dat", textDataPtr); + loadTextData("texte.dat"); if (g_cine->getGameType() == Cine::GType_OS && !(g_cine->getFeatures() & GF_DEMO)) { loadPoldatDat("poldat.dat"); @@ -142,8 +172,7 @@ void CineEngine::initialize() { freeAnimDataTable(); overlayList.clear(); messageTable.clear(); - - memset(objectTable, 0, sizeof(objectTable)); + resetObjectTable(); var8 = 0; diff --git a/engines/cine/cine.h b/engines/cine/cine.h index eaae555812..6011036eb1 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -59,7 +59,8 @@ enum CineGameType { enum CineGameFeatures { GF_CD = 1 << 0, GF_DEMO = 1 << 1, - GF_ALT_FONT = 1 << 2 + GF_ALT_FONT = 1 << 2, + GF_CRYPTED_BOOT_PRC = 1 << 3 }; struct CINEGameDescription; @@ -86,6 +87,8 @@ public: bool loadSaveDirectory(void); void makeSystemMenu(void); + int modifyGameSpeed(int speedChange); + int getTimerDelay() const; const CINEGameDescription *_gameDescription; Common::File _partFileHandle; @@ -109,6 +112,7 @@ private: void readVolCnf(); bool _preLoad; + int _timerDelayMultiplier; }; extern CineEngine *g_cine; diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index 8c940bcfd4..b15f30c35c 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -76,6 +76,25 @@ static const CINEGameDescription gameDescriptions[] = { 0, }, + // This is a CD version of Future Wars published by Sony. + // This version has a crypted AUTO00.PRC. + { + { + "fw", + "Sony CD version", + { + { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1}, + { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1}, + { NULL, 0, NULL, 0} + }, + Common::EN_USA, + Common::kPlatformPC, + Common::ADGF_CD + }, + GType_FW, + GF_CD | GF_CRYPTED_BOOT_PRC, + }, + { // This is the version included in the UK "Classic Collection" { @@ -251,6 +270,21 @@ static const CINEGameDescription gameDescriptions[] = { }, { + // This is a 16 color PC version (It came on three 720kB 3.5" disks). + // The protagonist is named John Glames in this version. + { + "os", + "", + AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"), + Common::EN_GRB, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + GType_OS, + 0, + }, + + { { "os", "", diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index cbddf0fc59..cb900e8850 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -31,12 +31,13 @@ #include "common/endian.h" #include "common/system.h" +#include "common/events.h" #include "graphics/cursorman.h" namespace Cine { -byte *page3Raw; +byte *collisionPage; FWRenderer *renderer = NULL; static const byte mouseCursorNormal[] = { @@ -91,7 +92,7 @@ static const byte cursorPalette[] = { */ FWRenderer::FWRenderer() : _background(NULL), _palette(NULL), _cmd(""), _cmdY(0), _messageBg(0), _backBuffer(new byte[_screenSize]), - _activeLowPal(NULL), _changePal(0) { + _activeLowPal(NULL), _changePal(0), _showCollisionPage(false) { assert(_backBuffer); @@ -125,6 +126,7 @@ void FWRenderer::clear() { _cmdY = 0; _messageBg = 0; _changePal = 0; + _showCollisionPage = false; } /*! \brief Draw 1bpp sprite using selected color @@ -225,14 +227,18 @@ void FWRenderer::drawCommand() { * \param x Top left message box corner coordinate * \param y Top left message box corner coordinate * \param width Message box width - * \param color Message box background color + * \param color Message box background color (Or if negative draws only the text) + * \note Negative colors are used in Operation Stealth's timed cutscenes + * (e.g. when first meeting The Movement for the Liberation of Santa Paragua). */ -void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte color) { +void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color) { int i, tx, ty, tw; int line = 0, words = 0, cw = 0; int space = 0, extraSpace = 0; - drawPlainBox(x, y, width, 4, color); + if (color >= 0) { + drawPlainBox(x, y, width, 4, color); + } tx = x + 4; ty = str[0] ? y - 5 : y + 4; tw = width - 8; @@ -252,7 +258,9 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte colo } ty += 9; - drawPlainBox(x, ty, width, 9, color); + if (color >= 0) { + drawPlainBox(x, ty, width, 9, color); + } tx = x + 4; } @@ -269,33 +277,56 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte colo } ty += 9; - drawPlainBox(x, ty, width, 4, color); - drawDoubleBorder(x, y, width, ty - y + 4, 2); + if (color >= 0) { + drawPlainBox(x, ty, width, 4, color); + drawDoubleBorder(x, y, width, ty - y + 4, 2); + } } /*! \brief Draw rectangle on screen * \param x Top left corner coordinate * \param y Top left corner coordinate - * \param width Rectangle width - * \param height Rectangle height + * \param width Rectangle width (Negative values draw the box horizontally flipped) + * \param height Rectangle height (Negative values draw the box vertically flipped) * \param color Fill color + * \note An on-screen rectangle's drawn width is always at least one. + * \note An on-screen rectangle's drawn height is always at least one. */ void FWRenderer::drawPlainBox(int x, int y, int width, int height, byte color) { - int i; - byte *dest = _backBuffer + y * 320 + x; + // Make width's and height's absolute values at least one + // which forces this function to always draw something if the + // drawing position is inside screen bounds. This fixes at least + // the showing of the oxygen gauge meter in Operation Stealth's + // first arcade sequence where this function is called with a + // height of zero. + if (width == 0) { + width = 1; + } + if (height == 0) { + height = 1; + } + // Handle horizontally flipped boxes if (width < 0) { - x += width; - width = -width; + width = ABS(width); + x -= width; } + // Handle vertically flipped boxes if (height < 0) { - y += height; - height = -height; + height = ABS(height); + y -= height; } - for (i = 0; i < height; i++) { - memset(dest + i * 320, color, width); + // Clip the rectangle to screen dimensions + Common::Rect boxRect(x, y, x + width, y + height); + Common::Rect screenRect(320, 200); + boxRect.clip(screenRect); + + // Draw the filled rectangle + byte *dest = _backBuffer + boxRect.top * 320 + boxRect.left; + for (int i = 0; i < boxRect.height(); i++) { + memset(dest + i * 320, color, boxRect.width()); } } @@ -335,8 +366,8 @@ int FWRenderer::drawChar(char character, int x, int y) { if (character == ' ') { x += 5; - } else if ((width = fontParamTable[(unsigned char)character].characterWidth)) { - idx = fontParamTable[(unsigned char)character].characterIdx; + } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) { + idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx; drawSpriteRaw(g_cine->_textHandler.textTable[idx][0], g_cine->_textHandler.textTable[idx][1], 16, 8, _backBuffer, x, y); x += width + 1; } @@ -405,7 +436,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { switch (it->type) { // color sprite case 0: - sprite = animDataTable + objectTable[it->objIdx].frame; + sprite = &animDataTable[objectTable[it->objIdx].frame]; len = sprite->_realWidth * sprite->_height; mask = new byte[len]; memcpy(mask, sprite->mask(), len); @@ -422,6 +453,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { _messageLen += messageTable[it->objIdx].size(); drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); + waitForPlayerClick = 1; break; // action failure message @@ -433,12 +465,13 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { width = width > 300 ? 300 : width; drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, 4); + waitForPlayerClick = 1; break; // bitmap case 4: assert(it->objIdx < NUM_MAX_OBJECT); - obj = objectTable + it->objIdx; + obj = &objectTable[it->objIdx]; if (obj->frame < 0) { return; @@ -480,16 +513,28 @@ void FWRenderer::drawFrame() { blit(); } +/*! + * \brief Turn on or off the showing of the collision page. + * If turned on the blitting routine shows the collision page instead of the back buffer. + * \note Useful for debugging collision page related problems. + */ +void FWRenderer::showCollisionPage(bool state) { + _showCollisionPage = state; +} + /*! \brief Update screen */ void FWRenderer::blit() { - g_system->copyRectToScreen(_backBuffer, 320, 0, 0, 320, 200); + // Show the back buffer or the collision page. Normally the back + // buffer but showing the collision page is useful for debugging. + byte *source = (_showCollisionPage ? collisionPage : _backBuffer); + g_system->copyRectToScreen(source, 320, 0, 0, 320, 200); } /*! \brief Set player command string * \param cmd New command string */ -void FWRenderer::setCommand(const char *cmd) { +void FWRenderer::setCommand(Common::String cmd) { _cmd = cmd; } @@ -617,6 +662,11 @@ void FWRenderer::saveBgNames(Common::OutSaveFile &fHandle) { fHandle.write(_bgName, 13); } +const char *FWRenderer::getBgName(uint idx) const { + assert(idx == 0); + return _bgName; +} + /*! \brief Restore active and backup palette from save * \param fHandle Savefile open for reading */ @@ -985,8 +1035,8 @@ int OSRenderer::drawChar(char character, int x, int y) { if (character == ' ') { x += 5; - } else if ((width = fontParamTable[(unsigned char)character].characterWidth)) { - idx = fontParamTable[(unsigned char)character].characterIdx; + } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) { + idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx; drawSpriteRaw2(g_cine->_textHandler.textTable[idx][0], 0, 16, 8, _backBuffer, x, y); x += width + 1; } @@ -1011,8 +1061,12 @@ void OSRenderer::drawBackground() { assert(scroll); - memcpy(_backBuffer, main + mainShift, mainSize); - memcpy(_backBuffer + mainSize, scroll, mainShift); + if (mainSize > 0) { // Just a precaution + memcpy(_backBuffer, main + mainShift, mainSize); + } + if (mainShift > 0) { // Just a precaution + memcpy(_backBuffer + mainSize, scroll, mainShift); + } } } @@ -1021,10 +1075,11 @@ void OSRenderer::drawBackground() { * \todo Add handling of type 22 overlays */ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { - int len; + int len, idx, width, height; objectStruct *obj; AnimData *sprite; byte *mask; + byte color; switch (it->type) { // color sprite @@ -1032,7 +1087,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { if (objectTable[it->objIdx].frame < 0) { break; } - sprite = animDataTable + objectTable[it->objIdx].frame; + sprite = &animDataTable[objectTable[it->objIdx].frame]; len = sprite->_realWidth * sprite->_height; mask = new byte[len]; generateMask(sprite->data(), mask, len, objectTable[it->objIdx].part); @@ -1041,6 +1096,32 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { delete[] mask; break; + // game message + case 2: + if (it->objIdx >= messageTable.size()) { + return; + } + + _messageLen += messageTable[it->objIdx].size(); + drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); + if (it->color >= 0) { // This test isn't in Future Wars's implementation + waitForPlayerClick = 1; + } + break; + + // action failure message + case 3: + idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3); + len = strlen(failureMessages[idx]); + _messageLen += len; + width = 6 * len + 20; + width = width > 300 ? 300 : width; + + // The used color here differs from Future Wars + drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, _messageBg); + waitForPlayerClick = 1; + break; + // bitmap case 4: if (objectTable[it->objIdx].frame >= 0) { @@ -1051,16 +1132,37 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { // masked background case 20: assert(it->objIdx < NUM_MAX_OBJECT); - obj = objectTable + it->objIdx; - sprite = animDataTable + obj->frame; + var5 = it->x; // A global variable updated here! + obj = &objectTable[it->objIdx]; + sprite = &animDataTable[obj->frame]; - if (obj->frame < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { + if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { break; } maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; + // FIXME: Implement correct drawing of type 21 overlays. + // Type 21 overlays aren't just filled rectangles, I found their drawing routine + // from Operation Stealth's drawSprite routine. So they're likely some kind of sprites + // and it's just a coincidence that the oxygen meter during the first arcade sequence + // works even somehow currently. I tried the original under DOSBox and the oxygen gauge + // is a long red bar that gets shorter as the air runs out. + case 21: + // A filled rectangle: + case 22: + // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing). + assert(it->objIdx < NUM_MAX_OBJECT); + obj = &objectTable[it->objIdx]; + color = obj->part & 0x0F; + width = obj->frame; + height = obj->costume; + drawPlainBox(obj->x, obj->y, width, height, color); + debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d", + it->type, obj->x, obj->y, width, height, color); + break; + // something else default: FWRenderer::renderOverlay(it); @@ -1332,6 +1434,11 @@ void OSRenderer::saveBgNames(Common::OutSaveFile &fHandle) { } } +const char *OSRenderer::getBgName(uint idx) const { + assert(idx < 9); + return _bgTable[idx].name; +} + /*! \brief Fade to black * \bug Operation Stealth sometimes seems to fade to black using * transformPalette resulting in double fadeout diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 6a3aa1ef89..c07214028c 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -62,13 +62,14 @@ protected: byte *_backBuffer; ///< Screen backbuffer uint16 *_activeLowPal; ///< Active 16 color palette int _changePal; ///< Load active palette to video backend on next frame + bool _showCollisionPage; ///< Should we show the collision page instead of the back buffer? Used for debugging. void fillSprite(const objectStruct &obj, uint8 color = 0); void drawMaskedSprite(const objectStruct &obj, const byte *mask); virtual void drawSprite(const objectStruct &obj); void drawCommand(); - void drawMessage(const char *str, int x, int y, int width, byte color); + void drawMessage(const char *str, int x, int y, int width, int color); void drawPlainBox(int x, int y, int width, int height, byte color); void drawBorder(int x, int y, int width, int height, byte color); void drawDoubleBorder(int x, int y, int width, int height, byte color); @@ -94,7 +95,7 @@ public: void drawFrame(); void blit(); - void setCommand(const char *cmd); + void setCommand(Common::String cmd); virtual void incrustMask(const objectStruct &obj, uint8 color = 0); virtual void incrustSprite(const objectStruct &obj); @@ -111,6 +112,7 @@ public: virtual uint getScroll() const; virtual void removeBg(unsigned int idx); virtual void saveBgNames(Common::OutSaveFile &fHandle); + virtual const char *getBgName(uint idx = 0) const; virtual void refreshPalette(); virtual void reloadPalette(); @@ -123,6 +125,7 @@ public: void drawInputBox(const char *info, const char *input, int cursor, int x, int y, int width); virtual void fadeToBlack(); + void showCollisionPage(bool state); }; /*! \brief Operation Stealth renderer @@ -168,6 +171,7 @@ public: uint getScroll() const; void removeBg(unsigned int idx); void saveBgNames(Common::OutSaveFile &fHandle); + const char *getBgName(uint idx = 0) const; void refreshPalette(); void reloadPalette(); @@ -181,7 +185,7 @@ public: void gfxDrawSprite(byte *src4, uint16 sw, uint16 sh, byte *dst4, int16 sx, int16 sy); -extern byte *page3Raw; +extern byte *collisionPage; extern FWRenderer *renderer; void setMouseCursor(int cursor); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index e5e670c973..e2402a4c8d 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -125,11 +125,74 @@ static void processEvent(Common::Event &event) { g_cine->makeSystemMenu(); } break; + case Common::KEYCODE_F11: + renderer->showCollisionPage(true); + break; + case Common::KEYCODE_MINUS: + case Common::KEYCODE_KP_MINUS: + g_cine->modifyGameSpeed(-1); // Slower + break; + case Common::KEYCODE_PLUS: + case Common::KEYCODE_KP_PLUS: + g_cine->modifyGameSpeed(+1); // Faster + break; + case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP4: + moveUsingKeyboard(-1, 0); // Left + break; + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP6: + moveUsingKeyboard(+1, 0); // Right + break; + case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: + moveUsingKeyboard(0, +1); // Up + break; + case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP2: + moveUsingKeyboard(0, -1); // Down + break; + case Common::KEYCODE_KP9: + moveUsingKeyboard(+1, +1); // Up & Right + break; + case Common::KEYCODE_KP7: + moveUsingKeyboard(-1, +1); // Up & Left + break; + case Common::KEYCODE_KP1: + moveUsingKeyboard(-1, -1); // Down & Left + break; + case Common::KEYCODE_KP3: + moveUsingKeyboard(+1, -1); // Down & Right + break; default: lastKeyStroke = event.kbd.keycode; break; } break; + case Common::EVENT_KEYUP: + switch (event.kbd.keycode) { + case Common::KEYCODE_F11: + renderer->showCollisionPage(false); + break; + case Common::KEYCODE_KP5: // Emulated left mouse button click + case Common::KEYCODE_LEFT: // Left + case Common::KEYCODE_KP4: // Left + case Common::KEYCODE_RIGHT: // Right + case Common::KEYCODE_KP6: // Right + case Common::KEYCODE_UP: // Up + case Common::KEYCODE_KP8: // Up + case Common::KEYCODE_DOWN: // Down + case Common::KEYCODE_KP2: // Down + case Common::KEYCODE_KP9: // Up & Right + case Common::KEYCODE_KP7: // Up & Left + case Common::KEYCODE_KP1: // Down & Left + case Common::KEYCODE_KP3: // Down & Right + // Stop ego movement made with keyboard when releasing a known key + moveUsingKeyboard(0, 0); + break; + default: + break; + } default: break; } @@ -138,7 +201,7 @@ static void processEvent(Common::Event &event) { void manageEvents() { Common::EventManager *eventMan = g_system->getEventManager(); - uint32 nextFrame = g_system->getMillis() + kGameTimerDelay * kGameSpeed; + uint32 nextFrame = g_system->getMillis() + g_cine->getTimerDelay(); do { Common::Event event; while (eventMan->pollEvent(event)) { @@ -227,7 +290,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { menuCommandLen = 0; playerCommand = -1; - strcpy(commandBuffer, ""); + commandBuffer = ""; globalVars[VAR_MOUSE_X_POS] = 0; globalVars[VAR_MOUSE_Y_POS] = 0; @@ -247,14 +310,44 @@ void CineEngine::mainLoop(int bootScriptIdx) { } do { + // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence. + // This makes it possible to pass the arcade sequence for now. + // FIXME: Remove the hack and make the first arcade sequence normally playable. + if (g_cine->getGameType() == Cine::GType_OS) { + Common::String bgName(renderer->getBgName()); + // Check if the background is one of the three backgrounds + // that are only used during the first arcade sequence. + if (bgName == "28.PI1" || bgName == "29.PI1" || bgName == "30.PI1") { + static const uint oxygenObjNum = 202, maxOxygen = 264; + // Force the amount of oxygen left to the maximum. + objectTable[oxygenObjNum].x = maxOxygen; + } + } + + // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck. + // After the first arcade sequence the player comes up stairs from + // the water in Santa Paragua's downtown in front of the flower shop. + // Previously he was completely stuck after getting up the stairs. + // If the background is the one used in the flower shop scene ("21.PI1") + // and the player is at the exact location after getting up the stairs + // then we just nudge him a tiny bit away from the stairs and voila, he's free! + // Maybe the real problem behind all this is collision data related as it looks + // like there's some boundary right there near position (204, 110) which we can + // jump over by moving the character to (204, 109). The script handling the + // flower shop scene is AIRPORT.PRC's 13th script. + // FIXME: Remove the hack and solve what's really causing the problem in the first place. + if (g_cine->getGameType() == Cine::GType_OS) { + if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && objectTable[1].x == 204 && objectTable[1].y == 110) { + objectTable[1].y--; // Move the player character upward on-screen by one pixel + } + } + stopMusicAfterFadeOut(); di = executePlayerInput(); // Clear the zoneQuery table (Operation Stealth specific) if (g_cine->getGameType() == Cine::GType_OS) { - for (uint i = 0; i < NUM_MAX_ZONE; i++) { - zoneQuery[i] = 0; - } + Common::set_to(zoneQuery.begin(), zoneQuery.end(), 0); } if (g_cine->getGameType() == Cine::GType_OS) { @@ -279,6 +372,11 @@ void CineEngine::mainLoop(int bootScriptIdx) { renderer->drawFrame(); } + // NOTE: In the original Future Wars and Operation Stealth messages + // were removed when running the drawOverlays function which is + // currently called from the renderer's drawFrame function. + removeMessages(); + if (waitForPlayerClick) { playerAction = false; @@ -308,8 +406,6 @@ void CineEngine::mainLoop(int bootScriptIdx) { } while (mouseButton != 0); waitForPlayerClick = 0; - - removeMessages(); } if (checkForPendingDataLoadSwitch) { diff --git a/engines/cine/main_loop.h b/engines/cine/main_loop.h index a2f828fd34..c729b324ca 100644 --- a/engines/cine/main_loop.h +++ b/engines/cine/main_loop.h @@ -28,11 +28,6 @@ namespace Cine { -enum { - kGameTimerDelay = 1000 / (1193180 / 10923), - kGameSpeed = 12 -}; - void mainLoop(int bootScriptIdx); void manageEvents(); diff --git a/engines/cine/msg.cpp b/engines/cine/msg.cpp index 55eb627309..c826db3bf3 100644 --- a/engines/cine/msg.cpp +++ b/engines/cine/msg.cpp @@ -34,29 +34,40 @@ namespace Cine { Common::StringList messageTable; void loadMsg(char *pMsgName) { - int i, count, len; - byte *ptr, *dataPtr; - const char *messagePtr; + uint32 sourceSize; checkDataDisk(-1); - messageTable.clear(); - - ptr = dataPtr = readBundleFile(findFileInBundle(pMsgName)); + byte *dataPtr = readBundleFile(findFileInBundle(pMsgName), &sourceSize); setMouseCursor(MOUSE_CURSOR_DISK); - count = READ_BE_UINT16(ptr); - ptr += 2; - - messagePtr = (const char*)(ptr + 2 * count); - - for (i = 0; i < count; i++) { - len = READ_BE_UINT16(ptr); - ptr += 2; - - messageTable.push_back(messagePtr); - messagePtr += len; + uint count = READ_BE_UINT16(dataPtr); + uint messageLenPos = 2; + uint messageDataPos = messageLenPos + 2 * count; + + // Read in the messages + for (uint i = 0; i < count; i++) { + // Read message's length + uint messageLen = READ_BE_UINT16(dataPtr + messageLenPos); + messageLenPos += 2; + + // Store the read message. + // This code works around input data that has empty strings residing outside the input + // buffer (e.g. message indexes 58-254 in BATEAU.MSG in PROCS08 in Operation Stealth). + if (messageDataPos < sourceSize) { + messageTable.push_back((const char *)(dataPtr + messageDataPos)); + } else { + if (messageLen > 0) { // Only warn about overflowing non-empty strings + warning("loadMsg(%s): message (%d. / %d) is overflowing the input buffer. Replacing it with an empty string", pMsgName, i + 1, count); + } else { + debugC(5, kCineDebugPart, "loadMsg(%s): empty message (%d. / %d) resides outside input buffer", pMsgName, i + 1, count); + } + // Message resides outside the input buffer so we replace it with an empty string + messageTable.push_back(""); + } + // Jump to the next message + messageDataPos += messageLen; } free(dataPtr); diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp index c02e01c8ce..9781975f7c 100644 --- a/engines/cine/object.cpp +++ b/engines/cine/object.cpp @@ -35,9 +35,16 @@ namespace Cine { -objectStruct objectTable[NUM_MAX_OBJECT]; +Common::Array<objectStruct> objectTable; Common::List<overlay> overlayList; +/*! \brief Resets all elements in the object table. */ +void resetObjectTable() { + for (Common::Array<objectStruct>::iterator it = objectTable.begin(); it != objectTable.end(); it++) { + it->clear(); + } +} + void loadObject(char *pObjectName) { uint16 numEntry; uint16 entrySize; diff --git a/engines/cine/object.h b/engines/cine/object.h index 7ad65eb75f..3bf6cdcc42 100644 --- a/engines/cine/object.h +++ b/engines/cine/object.h @@ -38,6 +38,17 @@ struct objectStruct { int16 costume; char name[20]; uint16 part; + + /*! \brief Sets all member variables to zero. */ + void clear() { + this->x = 0; + this->y = 0; + this->mask = 0; + this->frame = 0; + this->costume = 0; + memset(this->name, 0, sizeof(this->name)); + this->part = 0; + } }; struct overlay { @@ -52,10 +63,10 @@ struct overlay { #define NUM_MAX_OBJECT 255 #define NUM_MAX_VAR 255 -extern objectStruct objectTable[NUM_MAX_OBJECT]; - +extern Common::Array<objectStruct> objectTable; extern Common::List<overlay> overlayList; +void resetObjectTable(); void loadObject(char *pObjectName); void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4); void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue); diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp index 3e6f5adf40..7f6307c640 100644 --- a/engines/cine/pal.cpp +++ b/engines/cine/pal.cpp @@ -28,10 +28,7 @@ namespace Cine { -uint16 palEntriesCount; - -PalEntry *palPtr = NULL; - +Common::Array<PalEntry> palArray; static byte paletteBuffer1[16]; static byte paletteBuffer2[16]; @@ -41,27 +38,20 @@ void loadPal(const char *fileName) { removeExtention(buffer, fileName); strcat(buffer, ".PAL"); - - if (palPtr) { - free(palPtr); - palPtr = NULL; - } - - palEntriesCount = 0; + palArray.clear(); Common::File palFileHandle; if (!palFileHandle.open(buffer)) error("loadPal(): Cannot open file %s", fileName); - palEntriesCount = palFileHandle.readUint16LE(); + uint16 palEntriesCount = palFileHandle.readUint16LE(); palFileHandle.readUint16LE(); // entry size - palPtr = (PalEntry *)malloc(palEntriesCount * sizeof(PalEntry)); - assert(palPtr); - for (int i = 0; i < palEntriesCount; ++i) { - palFileHandle.read(palPtr[i].name, 10); - palFileHandle.read(palPtr[i].pal1, 16); - palFileHandle.read(palPtr[i].pal2, 16); + palArray.resize(palEntriesCount); + for (uint i = 0; i < palArray.size(); ++i) { + palFileHandle.read(palArray[i].name, 10); + palFileHandle.read(palArray[i].pal1, 16); + palFileHandle.read(palArray[i].pal2, 16); } palFileHandle.close(); } @@ -81,8 +71,8 @@ int16 findPaletteFromName(const char *fileName) { position++; } - for (i = 0; i < palEntriesCount; i++) { - if (!strcmp(buffer, palPtr[i].name)) { + for (i = 0; i < palArray.size(); i++) { + if (!strcmp(buffer, palArray[i].name)) { return i; } } @@ -105,9 +95,9 @@ void loadRelatedPalette(const char *fileName) { paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i; } } else { - assert(paletteIndex < palEntriesCount); - memcpy(paletteBuffer1, palPtr[paletteIndex].pal1, 16); - memcpy(paletteBuffer2, palPtr[paletteIndex].pal2, 16); + assert(paletteIndex < (int32)palArray.size()); + memcpy(paletteBuffer1, palArray[paletteIndex].pal1, 16); + memcpy(paletteBuffer2, palArray[paletteIndex].pal2, 16); } } diff --git a/engines/cine/pal.h b/engines/cine/pal.h index 768cf0d27d..819680973c 100644 --- a/engines/cine/pal.h +++ b/engines/cine/pal.h @@ -34,7 +34,7 @@ struct PalEntry { byte pal2[16]; }; -extern PalEntry *palPtr; +extern Common::Array<PalEntry> palArray; void loadPal(const char *fileName); diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp index 88f2dcef52..657471be4e 100644 --- a/engines/cine/part.cpp +++ b/engines/cine/part.cpp @@ -31,13 +31,10 @@ namespace Cine { -uint16 numElementInPart; - -PartBuffer *partBuffer; +Common::Array<PartBuffer> partBuffer; void loadPart(const char *partName) { - memset(partBuffer, 0, sizeof(PartBuffer) * NUM_MAX_PARTDATA); - numElementInPart = 0; + partBuffer.clear(); g_cine->_partFileHandle.close(); @@ -48,13 +45,14 @@ void loadPart(const char *partName) { setMouseCursor(MOUSE_CURSOR_DISK); - numElementInPart = g_cine->_partFileHandle.readUint16BE(); + uint16 numElementInPart = g_cine->_partFileHandle.readUint16BE(); + partBuffer.resize(numElementInPart); g_cine->_partFileHandle.readUint16BE(); // entry size if (currentPartName != partName) strcpy(currentPartName, partName); - for (uint16 i = 0; i < numElementInPart; i++) { + for (uint16 i = 0; i < partBuffer.size(); i++) { g_cine->_partFileHandle.read(partBuffer[i].partName, 14); partBuffer[i].offset = g_cine->_partFileHandle.readUint32BE(); partBuffer[i].packedSize = g_cine->_partFileHandle.readUint32BE(); @@ -190,7 +188,7 @@ void CineEngine::readVolCnf() { int16 findFileInBundle(const char *fileName) { if (g_cine->getGameType() == Cine::GType_OS) { // look first in currently loaded resource file - for (int i = 0; i < numElementInPart; i++) { + for (uint i = 0; i < partBuffer.size(); i++) { if (!scumm_stricmp(fileName, partBuffer[i].partName)) { return i; } @@ -204,7 +202,7 @@ int16 findFileInBundle(const char *fileName) { const char *part = (*it)._value; loadPart(part); } - for (int i = 0; i < numElementInPart; i++) { + for (uint i = 0; i < partBuffer.size(); i++) { if (!scumm_stricmp(fileName, partBuffer[i].partName)) { return i; } @@ -212,26 +210,35 @@ int16 findFileInBundle(const char *fileName) { return -1; } -void readFromPart(int16 idx, byte *dataPtr) { +void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize) { setMouseCursor(MOUSE_CURSOR_DISK); g_cine->_partFileHandle.seek(partBuffer[idx].offset, SEEK_SET); - g_cine->_partFileHandle.read(dataPtr, partBuffer[idx].packedSize); + g_cine->_partFileHandle.read(dataPtr, MIN(partBuffer[idx].packedSize, maxSize)); } -byte *readBundleFile(int16 foundFileIdx) { - assert(foundFileIdx >= 0 && foundFileIdx < numElementInPart); +byte *readBundleFile(int16 foundFileIdx, uint32 *size) { + assert(foundFileIdx >= 0 && foundFileIdx < (int32)partBuffer.size()); + bool error = false; byte *dataPtr = (byte *)calloc(partBuffer[foundFileIdx].unpackedSize, 1); - if (partBuffer[foundFileIdx].unpackedSize != partBuffer[foundFileIdx].packedSize) { - byte *unpackBuffer = (byte *)malloc(partBuffer[foundFileIdx].packedSize); - readFromPart(foundFileIdx, unpackBuffer); + readFromPart(foundFileIdx, dataPtr, partBuffer[foundFileIdx].unpackedSize); + if (partBuffer[foundFileIdx].unpackedSize > partBuffer[foundFileIdx].packedSize) { CineUnpacker cineUnpacker; - if (!cineUnpacker.unpack(unpackBuffer, partBuffer[foundFileIdx].packedSize, dataPtr, partBuffer[foundFileIdx].unpackedSize)) { - warning("Error unpacking '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName); - } - free(unpackBuffer); - } else { - readFromPart(foundFileIdx, dataPtr); + error = !cineUnpacker.unpack(dataPtr, partBuffer[foundFileIdx].packedSize, dataPtr, partBuffer[foundFileIdx].unpackedSize); + } else if (partBuffer[foundFileIdx].unpackedSize < partBuffer[foundFileIdx].packedSize) { + // Unpacked size of a file should never be less than its packed size + error = true; + } else { // partBuffer[foundFileIdx].unpackedSize == partBuffer[foundFileIdx].packedSize + debugC(5, kCineDebugPart, "Loaded non-compressed file '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName); + } + + if (error) { + warning("Error unpacking '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName); + } + + // Set the size variable if a pointer to it has been given + if (size != NULL) { + *size = partBuffer[foundFileIdx].unpackedSize; } return dataPtr; @@ -259,7 +266,13 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) { return data; } -byte *readFile(const char *filename) { +/*! \brief Rotate byte value to the left by n bits */ +byte rolByte(byte value, uint n) { + n %= 8; + return (byte) ((value << n) | (value >> (8 - n))); +} + +byte *readFile(const char *filename, bool crypted) { Common::File in; in.open(filename); @@ -272,6 +285,16 @@ byte *readFile(const char *filename) { byte *dataPtr = (byte *)malloc(size); in.read(dataPtr, size); + // The Sony published CD version of Future Wars has its + // AUTO00.PRC file's bytes rotated to the right by one. + // So we decode the so called crypting by rotating all + // the bytes to the left by one. + if (crypted) { + for (uint index = 0; index < size; index++) { + dataPtr[index] = rolByte(dataPtr[index], 1); + } + } + return dataPtr; } @@ -284,7 +307,7 @@ void dumpBundle(const char *fileName) { strcpy(tmpPart, currentPartName); loadPart(fileName); - for (int i = 0; i < numElementInPart; i++) { + for (uint i = 0; i < partBuffer.size(); i++) { byte *data = readBundleFile(i); debug(0, "%s", partBuffer[i].partName); diff --git a/engines/cine/part.h b/engines/cine/part.h index 2a979e4879..755f843b4a 100644 --- a/engines/cine/part.h +++ b/engines/cine/part.h @@ -37,18 +37,18 @@ struct PartBuffer { #define NUM_MAX_PARTDATA 255 -extern PartBuffer *partBuffer; +extern Common::Array<PartBuffer> partBuffer; void loadPart(const char *partName); void closePart(void); int16 findFileInBundle(const char *fileName); -void readFromPart(int16 idx, byte *dataPtr); +void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize); -byte *readBundleFile(int16 foundFileIdx); +byte *readBundleFile(int16 foundFileIdx, uint32 *size = NULL); byte *readBundleSoundFile(const char *entryName, uint32 *size = 0); -byte *readFile(const char *filename); +byte *readFile(const char *filename, bool crypted = false); void checkDataDisk(int16 param); diff --git a/engines/cine/prc.cpp b/engines/cine/prc.cpp index 27b1044620..5d789f9b3b 100644 --- a/engines/cine/prc.cpp +++ b/engines/cine/prc.cpp @@ -61,7 +61,7 @@ bool loadPrc(const char *pPrcName) { checkDataDisk(-1); if ((g_cine->getGameType() == Cine::GType_FW) && (!scumm_stricmp(pPrcName, BOOT_PRC_NAME) || !scumm_stricmp(pPrcName, "demo.prc"))) { - scriptPtr = dataPtr = readFile(pPrcName); + scriptPtr = dataPtr = readFile(pPrcName, (g_cine->getFeatures() & GF_CRYPTED_BOOT_PRC) != 0); } else { scriptPtr = dataPtr = readBundleFile(findFileInBundle(pPrcName)); } diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index e761a0c8e4..97f45488f2 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -1319,6 +1319,7 @@ int FWScript::o1_loadBg() { return 0; } +/*! \brief Load collision table data */ int FWScript::o1_loadCt() { const char *param = getNextString(); @@ -1789,7 +1790,14 @@ int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneI int16 result = 0; for (int16 i = 0; i < numZones; i++) { - idx = getZoneFromPositionRaw(page3Raw, lx + i, ly, 320); + // Don't try to read data in Operation Stealth if position isn't in 320x200 screen bounds. + if (g_cine->getGameType() == Cine::GType_OS) { + if ((lx + i) < 0 || (lx + i) > 319 || ly < 0 || ly > 199) { + continue; + } + } + + idx = getZoneFromPositionRaw(collisionPage, lx + i, ly, 320); assert(idx >= 0 && idx < NUM_MAX_ZONE); diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp index a764281758..e8f16ebfcc 100644 --- a/engines/cine/script_os.cpp +++ b/engines/cine/script_os.cpp @@ -365,6 +365,7 @@ FWScript *OSScriptInfo::create(const RawObjectScript &script, int16 index, const // OPERATION STEALTH opcodes // ------------------------------------------------------------------------ +/*! \brief Load collision table data */ int FWScript::o2_loadCt() { const char *param = getNextString(); @@ -636,7 +637,28 @@ int FWScript::o2_loadAbs() { const char *param2 = getNextString(); debugC(5, kCineDebugScript, "Line: %d: loadABS(%d,%s)", _line, param1, param2); - loadAbs(param2, param1); + // Load the resource to an absolute position + if (loadResource(param2, param1) == -1) { // Check if the loading failed + // WORKAROUND: In the 256 color PC version of Operation Stealth when + // walking out of the airport in Santa Paragua to the street the + // player character should be seen as a grey silhuette while walking + // behind the glass. But actually the player character is completely + // invisible when walking behind the glass because the animation files + // used are wrongly loaded. In AIRPORT.PRC's 6th script there are + // calls loadAbs("JOHN01.ANI", 73) and loadAbs("JOHN02.ANI", 37) to + // load the animations involved but no such files are found with the + // game. Corresponding SET-files are found though. As it worked and + // looked fine when I tried loading them instead of the missing ANI + // files I'm doing so here. NOTE: At least the German Amiga version + // of Operation Stealth seems to have all the files involved + // (JOHN01.ANI, JOHN02.ANI, JOHN01.SET and JOHN02.SET). + if (scumm_stricmp(param2, "JOHN01.ANI") == 0 && param1 == 73) { + loadResource("JOHN01.SET", param1); + } else if (scumm_stricmp(param2, "JOHN02.ANI") == 0 && param1 == 37) { + loadResource("JOHN02.SET", param1); + } + } + return 0; } diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index f26032fe98..3618350476 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -604,6 +604,7 @@ bool PCSoundFxPlayer::load(const char *song) { _instrumentsData[i] = NULL; char instrument[64]; + memset(instrument, 0, 64); // Clear the data first memcpy(instrument, _sfxData + 20 + i * 30, 12); instrument[63] = '\0'; diff --git a/engines/cine/texte.cpp b/engines/cine/texte.cpp index e4fd334926..33c16159ec 100644 --- a/engines/cine/texte.cpp +++ b/engines/cine/texte.cpp @@ -29,70 +29,53 @@ namespace Cine { -byte *textDataPtr; - const char **failureMessages; const CommandeType *defaultActionCommand; const CommandeType *systemMenu; const CommandeType *confirmMenu; const char **otherMessages; -const char *commandPrepositionOn; +const char *defaultCommandPreposition; +const char **commandPrepositionTable; void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency); -void loadTextData(const char *pFileName, byte *pDestinationBuffer) { - Common::File pFileHandle; - uint16 entrySize; - uint16 numEntry; - uint16 i; - byte *tempBuffer; - uint16 dataSize; - - assert(pFileName); - assert(pDestinationBuffer); +void loadTextData(const char *filename) { + Common::File fileHandle; + assert(filename); - if (!pFileHandle.open(pFileName)) - error("loadTextData(): Cannot open file %s", pFileName); + if (!fileHandle.open(filename)) + error("loadTextData(): Cannot open file %s", filename); - entrySize = pFileHandle.readUint16BE(); - numEntry = pFileHandle.readUint16BE(); + uint entrySize = fileHandle.readUint16BE(); + uint numEntry = fileHandle.readUint16BE(); - dataSize = numEntry * entrySize; - pFileHandle.read(pDestinationBuffer, numEntry * entrySize); - - tempBuffer = pDestinationBuffer; + uint sourceSize = numEntry * entrySize; + Common::Array<byte> source; + source.resize(sourceSize); + fileHandle.read(source.begin(), sourceSize); + const int fontHeight = 8; + const int fontWidth = (g_cine->getGameType() == Cine::GType_FW) ? 16 : 8; + uint numCharacters; + uint bytesPerCharacter; if (g_cine->getGameType() == Cine::GType_FW) { - int numCharacters; - if (g_cine->getFeatures() & GF_ALT_FONT) { - numCharacters = 85; - } else { - numCharacters = 78; - } - - dataSize = dataSize / numCharacters; - - loadRelatedPalette(pFileName); - - for (i = 0; i < numCharacters; i++) { - gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], tempBuffer, 16, 8); - generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], 16 * 8, 0); - tempBuffer += dataSize; - } + numCharacters = (g_cine->getFeatures() & GF_ALT_FONT) ? 85 : 78; + bytesPerCharacter = sourceSize / numCharacters; // TODO: Check if this could be replaced with fontWidth * fontHeight + loadRelatedPalette(filename); } else { - for (i = 0; i < 90; i++) { - gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], tempBuffer, 8, 8); - generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], 8 * 8, 0); - tempBuffer += 0x40; - } + numCharacters = 90; + bytesPerCharacter = fontWidth * fontHeight; } - pFileHandle.close(); -} + for (uint i = 0; i < numCharacters; i++) { + gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], &source[i * bytesPerCharacter], fontWidth, fontHeight); + generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], fontWidth * fontHeight, 0); + } -const CharacterEntry *fontParamTable; + fileHandle.close(); +} -const CharacterEntry fontParamTable_standard[256] = { +static const CharacterEntry fontParamTable_standard[NUM_FONT_CHARS] = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, @@ -129,7 +112,7 @@ const CharacterEntry fontParamTable_standard[256] = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }; -const CharacterEntry fontParamTable_alt[256] = { +static const CharacterEntry fontParamTable_alt[NUM_FONT_CHARS] = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, @@ -208,6 +191,16 @@ void initLanguage(Common::Language lang) { "NOACTION" }; + static const char *commandPrepositionTable_EN[] = { + "", // EXAMINE + "", // TAKE + "", // INVENTORY + "on", // USE + "", // OPERATE + "to", // SPEAK + "" // NOACTION + }; + static const CommandeType systemMenu_EN[] = { "Pause", "Restart Game", @@ -223,9 +216,8 @@ void initLanguage(Common::Language lang) { "PAUSE", "Loading | %s", "Loading canceled ...", - "No baclup in the drive...", - "Please enter the backup name", - "on" + "No backup in the drive...", + "Please enter the backup name" }; static const CommandeType confirmMenu_EN[] = { @@ -276,6 +268,16 @@ void initLanguage(Common::Language lang) { "NOACTION" }; + static const char *commandPrepositionTable_FR[] = { + "", // EXAMINER + "", // PRENDRE + "", // INVENTAIRE + "sur", // UTILISER + "", // ACTIONNER + "a", // PARLER + "" // NOACTION + }; + static const CommandeType systemMenu_FR[] = { "Pause", "Nouvelle partie", @@ -297,8 +299,7 @@ void initLanguage(Common::Language lang) { "Sauvegarde de | %s", "Sauvegarde Annul\x82""e ...", "Aucune sauvegarde dans le lecteur ...", - "Veuillez entrer le Nom de la Sauvegarde .", - "sur" + "Veuillez entrer le Nom de la Sauvegarde ." }; static const char *failureMessages_ES[] = { @@ -344,6 +345,16 @@ void initLanguage(Common::Language lang) { "NOACTION" }; + static const char *commandPrepositionTable_ES[] = { + "", // EXAMINAR + "", // COGER + "", // INVENTARIO + "donde", // USAR + "", // ACCIONAR + "a", // HABLAR + "" // NOACTION + }; + static const CommandeType systemMenu_ES[] = { "Pause", "Nueva partida", @@ -365,8 +376,7 @@ void initLanguage(Common::Language lang) { "Gabacion de| %s", "Rrabacion anulada", "No hay partidas grabadas en este disco...", - "Teclea el nombre de la partida grabada", - "donde" + "Teclea el nombre de la partida grabada" }; static const char *failureMessages_DE[] = { @@ -403,15 +413,25 @@ void initLanguage(Common::Language lang) { }; static const CommandeType defaultActionCommand_DE[] = { - "Pr\x81""fe", + "Pr\x81""fe", // FIXME? The third letter should be Latin Small Letter U with diaeresis "Nimm", "Bestand", "Benutze", - "Bet\x84tige", + "Bet\x84tige", // FIXME? The fourth letter should be Latin Small Letter A with diaeresis "Sprich", "NOACTION" }; + static const char *commandPrepositionTable_DE[] = { + "", // Prufe + "", // Nimm + "", // Bestand + "gegen", // Benutze + "", // Betatige + "a", // Sprich + "" // NOACTION + }; + static const CommandeType systemMenu_DE[] = { "Pause", "Spiel Neu Starten", @@ -433,8 +453,7 @@ void initLanguage(Common::Language lang) { "Er L\x84""dt | %s", "Ladevorgang Abgebrochen...", "Kein Backup im Laufwerk...", - "Geben Sie den Namen|der Sicherungsdiskette ein", - "gegen" + "Geben Sie den Namen|der Sicherungsdiskette ein" }; static const char *failureMessages_IT[] = { @@ -480,6 +499,16 @@ void initLanguage(Common::Language lang) { "NOACTION" }; + static const char *commandPrepositionTable_IT[] = { + "", // ESAMINARE + "", // PRENDERE + "", // INVENTARIO + "su", // UTILIZZARE + "", // AZIONARE + "a", // PARLARE + "" // NOACTION + }; + static const CommandeType systemMenu_IT[] = { "Pausa", "Parte nuova", @@ -501,8 +530,7 @@ void initLanguage(Common::Language lang) { "Caricamento di| %s", "Caricamento annullato...", "Nessun salvataggio su questo disco...", - "Vogliate accedere con il nome del salvataggio", - "su" + "Vogliate accedere con il nome del salvataggio" }; switch (lang) { @@ -512,7 +540,8 @@ void initLanguage(Common::Language lang) { systemMenu = systemMenu_FR; confirmMenu = confirmMenu_FR; otherMessages = otherMessages_FR; - commandPrepositionOn = otherMessages_FR[7]; + defaultCommandPreposition = commandPrepositionTable_FR[3]; + commandPrepositionTable = commandPrepositionTable_FR; break; case Common::ES_ESP: @@ -521,7 +550,8 @@ void initLanguage(Common::Language lang) { systemMenu = systemMenu_ES; confirmMenu = confirmMenu_ES; otherMessages = otherMessages_ES; - commandPrepositionOn = otherMessages_ES[7]; + defaultCommandPreposition = commandPrepositionTable_ES[3]; + commandPrepositionTable = commandPrepositionTable_ES; break; case Common::DE_DEU: @@ -530,7 +560,8 @@ void initLanguage(Common::Language lang) { systemMenu = systemMenu_DE; confirmMenu = confirmMenu_DE; otherMessages = otherMessages_DE; - commandPrepositionOn = otherMessages_DE[7]; + defaultCommandPreposition = commandPrepositionTable_DE[3]; + commandPrepositionTable = commandPrepositionTable_DE; break; case Common::IT_ITA: @@ -539,7 +570,8 @@ void initLanguage(Common::Language lang) { systemMenu = systemMenu_IT; confirmMenu = confirmMenu_IT; otherMessages = otherMessages_IT; - commandPrepositionOn = otherMessages_IT[7]; + defaultCommandPreposition = commandPrepositionTable_IT[3]; + commandPrepositionTable = commandPrepositionTable_IT; break; default: @@ -548,14 +580,17 @@ void initLanguage(Common::Language lang) { systemMenu = systemMenu_EN; confirmMenu = confirmMenu_EN; otherMessages = otherMessages_EN; - commandPrepositionOn = otherMessages_EN[7]; + defaultCommandPreposition = commandPrepositionTable_EN[3]; + commandPrepositionTable = commandPrepositionTable_EN; break; } if (g_cine->getFeatures() & GF_ALT_FONT) { - fontParamTable = fontParamTable_alt; + // Copy alternative font parameter table to the current font parameter table + Common::copy(fontParamTable_alt, fontParamTable_alt + NUM_FONT_CHARS, g_cine->_textHandler.fontParamTable); } else { - fontParamTable = fontParamTable_standard; + // Copy standard font parameter to the current font parameter table + Common::copy(fontParamTable_standard, fontParamTable_standard + NUM_FONT_CHARS, g_cine->_textHandler.fontParamTable); } } @@ -590,25 +625,16 @@ void loadPoldatDat(const char *fname) { in.open(fname); if (in.isOpen()) { - CharacterEntry *ptr = (CharacterEntry *)malloc(sizeof(CharacterEntry) * 256); - - for (int i = 0; i < 256; i++) { - ptr[i].characterIdx = (int)in.readByte(); - ptr[i].characterWidth = (int)in.readByte(); + for (int i = 0; i < NUM_FONT_CHARS; i++) { + g_cine->_textHandler.fontParamTable[i].characterIdx = in.readByte(); + g_cine->_textHandler.fontParamTable[i].characterWidth = in.readByte(); } - fontParamTable = ptr; - in.close(); } else { error("Cannot open file %s for reading", fname); } } -void freePoldatDat() { - free(const_cast<Cine::CharacterEntry *>(fontParamTable)); - fontParamTable = 0; -} - /*! \brief Fit a substring of text into one line of fixed width text box * \param str Text to fit * \param maxWidth Text box width @@ -633,7 +659,7 @@ int fitLine(const char *str, int maxWidth, int &words, int &width) { bkpWidth = width; bkpLen = i + 1; } else { - charWidth = fontParamTable[(unsigned char)str[i]].characterWidth + 1; + charWidth = g_cine->_textHandler.fontParamTable[(unsigned char)str[i]].characterWidth + 1; width += charWidth; } diff --git a/engines/cine/texte.h b/engines/cine/texte.h index f471c3c49e..bc4beac492 100644 --- a/engines/cine/texte.h +++ b/engines/cine/texte.h @@ -33,10 +33,17 @@ namespace Cine { typedef char CommandeType[20]; -extern byte *textDataPtr; +// Number of characters in a font +#define NUM_FONT_CHARS 256 + +struct CharacterEntry { + byte characterIdx; + byte characterWidth; +}; struct TextHandler { - byte textTable[256][2][16 * 8]; + byte textTable[NUM_FONT_CHARS][2][16 * 8]; + CharacterEntry fontParamTable[NUM_FONT_CHARS]; }; extern const char **failureMessages; @@ -44,20 +51,13 @@ extern const CommandeType *defaultActionCommand; extern const CommandeType *systemMenu; extern const CommandeType *confirmMenu; extern const char **otherMessages; -extern const char *commandPrepositionOn; - -struct CharacterEntry { - byte characterIdx; - byte characterWidth; -}; - -extern const CharacterEntry *fontParamTable; +extern const char *defaultCommandPreposition; +extern const char **commandPrepositionTable; -void loadTextData(const char *pFileName, byte *pDestinationBuffer); +void loadTextData(const char *filename); void loadErrmessDat(const char *fname); void freeErrmessDat(void); void loadPoldatDat(const char *fname); -void freePoldatDat(void); int fitLine(const char *ptr, int maxWidth, int &words, int &width); diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 2fcb015fcd..e96e03b03c 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -78,7 +78,7 @@ byte _danKeysPressed; int16 playerCommand; -char commandBuffer[80]; +Common::String commandBuffer; char currentPrcName[20]; char currentRelName[20]; char currentObjectName[20]; @@ -95,11 +95,23 @@ int16 saveVar2; byte isInPause = 0; -// TODO: Implement inputVar0's changes in the program -// Currently inputVar0 isn't updated anywhere even though it's used at least in processSeqListElement. -uint16 inputVar0 = 0; -byte inputVar1 = 0; -uint16 inputVar2 = 0, inputVar3 = 0; +/*! \brief Values used by the xMoveKeyb variable */ +enum xMoveKeybEnums { + kKeybMoveCenterX = 0, + kKeybMoveRight = 1, + kKeybMoveLeft = 2 +}; + +/*! \brief Values used by the yMoveKeyb variable */ +enum yMoveKeybEnums { + kKeybMoveCenterY = 0, + kKeybMoveDown = 1, + kKeybMoveUp = 2 +}; + +uint16 xMoveKeyb = kKeybMoveCenterX; +bool egoMovedWithKeyboard = false; +uint16 yMoveKeyb = kKeybMoveCenterY; SelectedObjStruct currentSelectedObject; @@ -114,9 +126,34 @@ CommandeType objectListCommand[20]; int16 objListTab[20]; uint16 exitEngine; -uint16 zoneData[NUM_MAX_ZONE]; -uint16 zoneQuery[NUM_MAX_ZONE]; //!< Only exists in Operation Stealth +Common::Array<uint16> zoneData; +Common::Array<uint16> zoneQuery; //!< Only exists in Operation Stealth + +/*! \brief Move the player character using the keyboard + * \param x Negative values move left, positive right, zero not at all + * \param y Negative values move down, positive up, zero not at all + * NOTE: If both x and y are zero then the character stops + * FIXME: This seems to only work in Operation Stealth. May need code changes somewhere else... + */ +void moveUsingKeyboard(int x, int y) { + if (x > 0) { + xMoveKeyb = kKeybMoveRight; + } else if (x < 0) { + xMoveKeyb = kKeybMoveLeft; + } else { + xMoveKeyb = kKeybMoveCenterX; + } + + if (y > 0) { + yMoveKeyb = kKeybMoveUp; + } else if (y < 0) { + yMoveKeyb = kKeybMoveDown; + } else { + yMoveKeyb = kKeybMoveCenterY; + } + egoMovedWithKeyboard = x || y; +} void stopMusicAfterFadeOut(void) { // if (g_sfxPlayer->_fadeOutCounter != 0 && g_sfxPlayer->_fadeOutCounter < 100) { @@ -141,7 +178,6 @@ void addPlayerCommandMessage(int16 cmd) { tmp.type = 3; overlayList.push_back(tmp); - waitForPlayerClick = 1; } int16 getRelEntryForObject(uint16 param1, uint16 param2, SelectedObjStruct *pSelectedObject) { @@ -189,6 +225,15 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) { frame = ABS((int16)(objectTable[it->objIdx].frame)); part = objectTable[it->objIdx].part; + // Additional case for negative frame values in Operation Stealth + if (g_cine->getGameType() == Cine::GType_OS && objectTable[it->objIdx].frame < 0) { + if ((it->type == 1) && (x >= objX) && (objX + frame >= x) && (y >= objY) && (objY + part >= y)) { + return it->objIdx; + } else { + continue; + } + } + if (it->type == 0) { threshold = animDataTable[frame]._var1; } else { @@ -201,16 +246,19 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) { xdif = x - objX; ydif = y - objY; - if ((xdif < 0) || ((threshold << 4) <= xdif) || (ydif < 0) || (ydif >= height) || !animDataTable[frame].data()) { + if ((xdif < 0) || ((threshold << 4) <= xdif) || (ydif <= 0) || (ydif >= height) || !animDataTable[frame].data()) { continue; } if (g_cine->getGameType() == Cine::GType_OS) { + // This test isn't present in Operation Stealth's PC version's disassembly + // but removing it makes things crash sometimes (e.g. when selecting a verb + // and moving the mouse cursor around the floor in the airport's bathroom). if (xdif >= width) { continue; } - if (it->type == 0 && animDataTable[frame].getColor(xdif, ydif) != part) { + if (it->type == 0 && animDataTable[frame].getColor(xdif, ydif) != (part & 0x0F)) { return it->objIdx; } else if (it->type == 1 && gfxGetBit(xdif, ydif, animDataTable[frame].data(), animDataTable[frame]._width * 4)) { return it->objIdx; @@ -270,6 +318,18 @@ void saveCommandVariables(Common::OutSaveFile &out) { } } +/*! \brief Save the 80 bytes long command buffer padded to that length with zeroes. */ +void saveCommandBuffer(Common::OutSaveFile &out) { + // Let's make sure there's space for the trailing zero + // (That's why we subtract one from the maximum command buffer size here). + uint32 size = MIN<uint32>(commandBuffer.size(), kMaxCommandBufferSize - 1); + out.write(commandBuffer.c_str(), size); + // Write the rest as zeroes (Here we also write the string's trailing zero) + for (uint i = 0; i < kMaxCommandBufferSize - size; i++) { + out.writeByte(0); + } +} + void saveAnimDataTable(Common::OutSaveFile &out) { out.writeUint16BE(NUM_MAX_ANIMDATA); // Entry count out.writeUint16BE(0x1E); // Entry size @@ -570,16 +630,7 @@ void CineEngine::resetEngine() { relTable.clear(); scriptTable.clear(); messageTable.clear(); - - for (int i = 0; i < NUM_MAX_OBJECT; i++) { - objectTable[i].x = 0; - objectTable[i].y = 0; - objectTable[i].part = 0; - objectTable[i].name[0] = 0; - objectTable[i].frame = 0; - objectTable[i].mask = 0; - objectTable[i].costume = 0; - } + resetObjectTable(); globalVars.reset(); @@ -596,7 +647,7 @@ void CineEngine::resetEngine() { playerCommand = -1; isDrawCommandEnabled = 0; - strcpy(commandBuffer, ""); + commandBuffer = ""; globalVars[VAR_MOUSE_X_POS] = 0; globalVars[VAR_MOUSE_Y_POS] = 0; @@ -797,7 +848,10 @@ bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) { globalVars.load(in, NUM_MAX_VAR); loadZoneData(in); loadCommandVariables(in); - in.read(commandBuffer, 0x50); + char tempCommandBuffer[kMaxCommandBufferSize]; + in.read(tempCommandBuffer, kMaxCommandBufferSize); + commandBuffer = tempCommandBuffer; + renderer->setCommand(commandBuffer); loadZoneQuery(in); // TODO: Use the loaded string (Current music name (String, 13 bytes)). @@ -934,7 +988,9 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor loadCommandVariables(in); // At 0x22A9 (i.e. 0x22A1 + 4 * 2): - in.read(commandBuffer, 0x50); + char tempCommandBuffer[kMaxCommandBufferSize]; + in.read(tempCommandBuffer, kMaxCommandBufferSize); + commandBuffer = tempCommandBuffer; renderer->setCommand(commandBuffer); // At 0x22F9 (i.e. 0x22A9 + 0x50): @@ -1082,7 +1138,7 @@ void CineEngine::makeSaveFW(Common::OutSaveFile &out) { globalVars.save(out, NUM_MAX_VAR); saveZoneData(out); saveCommandVariables(out); - out.write(commandBuffer, 0x50); + saveCommandBuffer(out); out.writeUint16BE(renderer->_cmdY); out.writeUint16BE(bgVar0); @@ -1297,7 +1353,7 @@ void CineEngine::makeSaveOS(Common::OutSaveFile &out) { globalVars.save(out, NUM_MAX_VAR); saveZoneData(out); saveCommandVariables(out); - out.write(commandBuffer, 0x50); + saveCommandBuffer(out); saveZoneQuery(out); // FIXME: Save a proper name here, saving an empty string currently. @@ -1360,19 +1416,38 @@ void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 c } void processInventory(int16 x, int16 y) { - int16 listSize = buildObjectListCommand(-2); uint16 button; + int menuWidth; + int listSize; + int commandParam; + + if (g_cine->getGameType() == Cine::GType_FW) { + menuWidth = 140; + commandParam = -2; + } else { // Operation Stealth + menuWidth = 160; + commandParam = -3; + } + + listSize = buildObjectListCommand(commandParam); if (!listSize) return; - renderer->drawMenu(objectListCommand, listSize, x, y, 140, -1); + renderer->drawMenu(objectListCommand, listSize, x, y, menuWidth, -1); renderer->blit(); do { manageEvents(); getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16); } while (!button); + + do { + manageEvents(); + getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16); + } while (button); + + // TODO: Both Future Wars and Operation Stealth call showMouse, drawMouse or something similar here. } int16 buildObjectListCommand(int16 param) { @@ -1416,6 +1491,8 @@ int16 selectSubObject(int16 x, int16 y, int16 param) { return objListTab[selectedObject]; } +// TODO: Make separate functions for Future Wars's and Operation Stealth's version of this function, this is getting too messy +// TODO: Add support for using the different prepositions for different verbs (Doesn't work currently) void makeCommandLine(void) { uint16 x, y; @@ -1423,9 +1500,9 @@ void makeCommandLine(void) { commandVar2 = -10; if (playerCommand != -1) { - strcpy(commandBuffer, defaultActionCommand[playerCommand]); + commandBuffer = defaultActionCommand[playerCommand]; } else { - strcpy(commandBuffer, ""); + commandBuffer = ""; } if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection ? @@ -1440,8 +1517,12 @@ void makeCommandLine(void) { } if (si < 0) { - playerCommand = -1; - strcpy(commandBuffer, ""); + if (g_cine->getGameType() == Cine::GType_OS) { + canUseOnObject = 0; + } else { // Future Wars + playerCommand = -1; + commandBuffer = ""; + } } else { if (g_cine->getGameType() == Cine::GType_OS) { if (si >= 8000) { @@ -1454,23 +1535,28 @@ void makeCommandLine(void) { commandVar3[0] = si; commandVar1 = 1; - - strcat(commandBuffer, " "); - strcat(commandBuffer, objectTable[commandVar3[0]].name); - strcat(commandBuffer, " "); - strcat(commandBuffer, commandPrepositionOn); + commandBuffer += " "; + commandBuffer += objectTable[commandVar3[0]].name; + commandBuffer += " "; + if (g_cine->getGameType() == Cine::GType_OS) { + commandBuffer += commandPrepositionTable[playerCommand]; + } else { // Future Wars + commandBuffer += defaultCommandPreposition; + } } - } else { + } + + if (g_cine->getGameType() == Cine::GType_OS || !(playerCommand != -1 && choiceResultTable[playerCommand] == 2)) { if (playerCommand == 2) { getMouseData(mouseUpdateStatus, &dummyU16, &x, &y); processInventory(x, y + 8); playerCommand = -1; commandVar1 = 0; - strcpy(commandBuffer, ""); + commandBuffer = ""; } } - if (g_cine->getGameType() == Cine::GType_OS) { + if (g_cine->getGameType() == Cine::GType_OS && playerCommand != 2) { if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object int16 si; @@ -1478,34 +1564,38 @@ void makeCommandLine(void) { si = selectSubObject(x, y + 8, -subObjectUseTable[playerCommand]); - if (si) { + if (si >= 0) { if (si >= 8000) { si -= 8000; } commandVar3[commandVar1] = si; - commandVar1++; - - // TODO: add command message draw + commandBuffer += " "; + commandBuffer += objectTable[si].name; } + } - isDrawCommandEnabled = 1; + isDrawCommandEnabled = 1; - if (playerCommand != -1 && choiceResultTable[playerCommand] == commandVar1) { - SelectedObjStruct obj; - obj.idx = commandVar3[0]; - obj.param = commandVar3[1]; - int16 di = getRelEntryForObject(playerCommand, commandVar1, &obj); + if (playerCommand != -1 && choiceResultTable[playerCommand] == commandVar1) { + SelectedObjStruct obj; + obj.idx = commandVar3[0]; + obj.param = commandVar3[1]; + int16 di = getRelEntryForObject(playerCommand, commandVar1, &obj); - if (di != -1) { - runObjectScript(di); - } - } + if (di != -1) { + runObjectScript(di); + } // TODO: else addFailureMessage(playerCommand) + + playerCommand = -1; + commandVar1 = 0; + commandBuffer = ""; } } - if (!disableSystemMenu) { + if (g_cine->getGameType() == Cine::GType_OS || !disableSystemMenu) { + isDrawCommandEnabled = 1; renderer->setCommand(commandBuffer); } } @@ -1678,6 +1768,11 @@ uint16 executePlayerInput(void) { } if (allowPlayerInput) { + if (isDrawCommandEnabled) { + renderer->setCommand(commandBuffer); + isDrawCommandEnabled = 0; + } + getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); while (mouseButton && currentEntry < 200) { @@ -1716,8 +1811,9 @@ uint16 executePlayerInput(void) { commandVar3[commandVar1] = si; commandVar1++; - strcat(commandBuffer, " "); - strcat(commandBuffer, objectTable[si].name); + commandBuffer += " "; + commandBuffer += objectTable[si].name; + isDrawCommandEnabled = 1; @@ -1739,8 +1835,8 @@ uint16 executePlayerInput(void) { playerCommand = -1; commandVar1 = 0; - strcpy(commandBuffer, ""); - renderer->setCommand(""); + commandBuffer = ""; + renderer->setCommand(commandBuffer); } } else { globalVars[VAR_MOUSE_X_POS] = mouseX; @@ -1761,13 +1857,7 @@ uint16 executePlayerInput(void) { if (commandVar2 != objIdx) { if (objIdx != -1) { - char command[256]; - - strcpy(command, commandBuffer); - strcat(command, " "); - strcat(command, objectTable[objIdx].name); - - renderer->setCommand(command); + renderer->setCommand(commandBuffer + " " + objectTable[objIdx].name); } else { isDrawCommandEnabled = 1; } @@ -1858,8 +1948,8 @@ uint16 executePlayerInput(void) { var_2 = 0; } - if (inputVar1 && allowPlayerInput) { // use keyboard - inputVar1 = 0; + if (egoMovedWithKeyboard && allowPlayerInput) { // use keyboard + egoMovedWithKeyboard = false; switch (globalVars[VAR_MOUSE_X_MODE]) { case 1: @@ -1891,8 +1981,8 @@ uint16 executePlayerInput(void) { globalVars[VAR_MOUSE_X_POS] = mouseX; globalVars[VAR_MOUSE_Y_POS] = mouseY; } else { - if (inputVar2) { - if (inputVar2 == 2) { + if (xMoveKeyb) { + if (xMoveKeyb == kKeybMoveLeft) { globalVars[VAR_MOUSE_X_POS] = 1; } else { globalVars[VAR_MOUSE_X_POS] = 320; @@ -1901,8 +1991,8 @@ uint16 executePlayerInput(void) { globalVars[VAR_MOUSE_X_POS] = mouseX; } - if (inputVar3) { - if (inputVar3 == 2) { + if (yMoveKeyb) { + if (yMoveKeyb == kKeybMoveUp) { globalVars[VAR_MOUSE_Y_POS] = 1; } else { globalVars[VAR_MOUSE_Y_POS] = 200; @@ -1943,6 +2033,14 @@ uint16 executePlayerInput(void) { } } + // Update Operation Stealth specific global variables. + // This fixes swimming at the bottom of the ocean after + // having been thrown into it with the girl. + if (g_cine->getGameType() == Cine::GType_OS) { + globalVars[251] = globalVars[VAR_MOUSE_X_POS]; + globalVars[252] = globalVars[VAR_MOUSE_Y_POS]; + } + return var_5E; } @@ -1984,9 +2082,22 @@ void drawSprite(Common::List<overlay>::iterator it, const byte *spritePtr, const void removeMessages() { Common::List<overlay>::iterator it; + bool remove; for (it = overlayList.begin(); it != overlayList.end(); ) { - if (it->type == 2 || it->type == 3) { + if (g_cine->getGameType() == Cine::GType_OS) { + // NOTE: These are really removeOverlay calls that have been deferred. + // In Operation Stealth's disassembly elements are removed from the + // overlay list right in the drawOverlays function (And actually in + // some other places too) and that's where incrementing a the overlay's + // last parameter by one if it's negative and testing it for positivity + // comes from too. + remove = it->type == 3 || (it->type == 2 && (it->color >= 0 || ++it->color >= 0)); + } else { // Future Wars + remove = it->type == 2 || it->type == 3; + } + + if (remove) { it = overlayList.erase(it); } else { ++it; @@ -2069,7 +2180,6 @@ void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 par tmp.color = param5; overlayList.push_back(tmp); - waitForPlayerClick = 1; } Common::List<SeqListElement> seqList; @@ -2328,9 +2438,9 @@ void processSeqListElement(SeqListElement &element) { } computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, x2, y2); } else { - if (inputVar0 && allowPlayerInput) { + if (xMoveKeyb && allowPlayerInput) { int16 adder = param1 + 1; - if (inputVar0 != 1) { + if (xMoveKeyb != kKeybMoveRight) { adder = -adder; } // FIXME: In Operation Stealth's disassembly global variable 251 is used here @@ -2339,9 +2449,9 @@ void processSeqListElement(SeqListElement &element) { globalVars[VAR_MOUSE_X_POS] = globalVars[251] = ptr1[4] + x + adder; } - if (inputVar1 && allowPlayerInput) { + if (yMoveKeyb && allowPlayerInput) { int16 adder = param2 + 1; - if (inputVar1 != 1) { + if (yMoveKeyb != kKeybMoveDown) { adder = -adder; } // TODO: Name currently unnamed global variable 252 diff --git a/engines/cine/various.h b/engines/cine/various.h index d87679ca08..0ee77c1b47 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -33,6 +33,9 @@ namespace Cine { +// Maximum size of the command buffer including the trailing zero +#define kMaxCommandBufferSize 80 + void initLanguage(Common::Language lang); int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, uint16 Y, uint16 width, bool recheckValue = false); @@ -85,7 +88,7 @@ extern byte _danKeysPressed; extern int16 playerCommand; -extern char commandBuffer[80]; +extern Common::String commandBuffer; extern char currentPrcName[20]; extern char currentRelName[20]; @@ -129,8 +132,8 @@ struct SelectedObjStruct { }; #define NUM_MAX_ZONE 16 -extern uint16 zoneData[NUM_MAX_ZONE]; -extern uint16 zoneQuery[NUM_MAX_ZONE]; +extern Common::Array<uint16> zoneData; +extern Common::Array<uint16> zoneQuery; void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 param5); @@ -145,6 +148,7 @@ void processSeqList(void); void resetGfxEntityEntry(uint16 objIdx); bool makeTextEntryMenu(const char *caption, char *string, int strLen, int y); +void moveUsingKeyboard(int x, int y); } // End of namespace Cine diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp index a426857fbd..9832d58294 100644 --- a/engines/drascula/detection.cpp +++ b/engines/drascula/detection.cpp @@ -195,6 +195,38 @@ static const DrasculaGameDescription gameDescriptions[] = { }, }, + { + // Drascula Spanish version (ScummVM repacked files) + { + "drascula", + 0, + { + {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563}, + {"packet.004", 0, "a289d3cf80d50f25ec569b653248437e", 17205838}, + {NULL, 0, NULL, 0} + }, + Common::ES_ESP, + Common::kPlatformPC, + GF_PACKED + }, + }, + + { + // Drascula Italian version (ScummVM repacked files) + { + "drascula", + 0, + { + {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563}, + {"packet.005", 0, "f80e10e37000a2201eabf8dad82c7f64", 16184223}, + {NULL, 0, NULL, 0} + }, + Common::IT_ITA, + Common::kPlatformPC, + GF_PACKED + }, + }, + { AD_TABLE_END_MARKER } }; diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 1cbe2ae0e1..2d24978f21 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -162,6 +162,12 @@ int DrasculaEngine::init() { _textmisc = 0; _textd1 = 0; + _color = 0; + blinking = 0; + leftMouseButton = 0; + rightMouseButton = 0; + *textName = 0; + if (!loadDrasculaDat()) return 1; @@ -286,6 +292,8 @@ void DrasculaEngine::endChapter() { bool DrasculaEngine::runCurrentChapter() { int n; + rightMouseButton = 0; + if (_lang == kSpanish) textSurface = extraSurface; else @@ -457,23 +465,45 @@ bool DrasculaEngine::runCurrentChapter() { playMusic(roomMusic); } + delay(25); +#ifndef _WIN32_WCE + // FIXME + // This and the following #ifndefs disable the excess updateEvents() calls *within* the game loop. + // Events such as keypresses or mouse clicks are dropped on the ground with no processing + // by these calls. They are properly handled by the implicit call through getScan() below. + // It is not a good practice to not process events and indeed this created problems with synthesized + // events in the wince port. updateEvents(); +#endif if (menuScreen == 0 && takeObject == 1) checkObjects(); +#ifdef _WIN32_WCE + if (rightMouseButton) + if (menuScreen) { +#else if (rightMouseButton == 1 && menuScreen == 1) { - delay(100); +#endif if (currentChapter == 2) loadPic(menuBackground, backSurface); else loadPic(99, backSurface); setPalette((byte *)&gamePalette); menuScreen = 0; +#ifndef _WIN32_WCE + // FIXME: This call here is in hope that it will catch the rightmouseup event so the + // next if block won't be executed. This too is not a good coding practice. I've recoded it + // with a mutual exclusive if block for the menu. I would commit this properly but I cannot test + // for other (see Desktop) ports right now. updateEvents(); +#endif +#ifdef _WIN32_WCE + } else { +#else } if (rightMouseButton == 1 && menuScreen == 0) { - delay(100); +#endif characterMoved = 0; if (trackProtagonist == 2) trackProtagonist = 1; @@ -486,15 +516,15 @@ bool DrasculaEngine::runCurrentChapter() { else loadPic("icons.alg", backSurface); menuScreen = 1; +#ifndef _WIN32_WCE updateEvents(); +#endif withoutVerb(); } if (leftMouseButton == 1 && menuBar == 1) { - delay(100); selectVerbFromBar(); } else if (leftMouseButton == 1 && takeObject == 0) { - delay(100); if (verify1()) return true; } else if (leftMouseButton == 1 && takeObject == 1) { @@ -674,7 +704,11 @@ void DrasculaEngine::updateEvents() { AudioCD.updateCD(); +#ifdef _WIN32_WCE + if (eventMan->pollEvent(event)) { +#else while (eventMan->pollEvent(event)) { +#endif switch (event.type) { case Common::EVENT_KEYDOWN: _keyPressed = event.kbd; @@ -987,9 +1021,14 @@ char ***DrasculaEngine::loadTexts(Common::File &in) { } void DrasculaEngine::freeTexts(char ***ptr) { + if (!ptr) + return; + for (int lang = 0; lang < _numLangs; lang++) { - free(ptr[lang][0] - DATAALIGNMENT); - free(ptr[lang]); + if (ptr[lang]) { + free(ptr[lang][0]); + free(ptr[lang]); + } } free(ptr); } diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp index 6e86788007..ef1d1cc7a3 100644 --- a/engines/drascula/interface.cpp +++ b/engines/drascula/interface.cpp @@ -114,7 +114,8 @@ void DrasculaEngine::clearMenu() { } void DrasculaEngine::enterName() { - Common::KeyCode key; + Common::KeyCode key, prevkey = Common::KEYCODE_INVALID; + int counter = 0; int v = 0, h = 0; char select2[23]; strcpy(select2, " "); @@ -123,17 +124,25 @@ void DrasculaEngine::enterName() { copyBackground(115, 14, 115, 14, 176, 9, bgSurface, screenSurface); print_abc(select2, 117, 15); updateScreen(); + _system->delayMillis(100); + key = getScan(); - delay(70); - if (key != 0) { + + // Artifically decrease repeat rate. + // Part of bug fix#2017432 DRASCULA: Typing is slow when you save a game + // Alternative is to roll our own event loop + if (key == prevkey) + if (++counter == 3) { + counter = 0; + prevkey = Common::KEYCODE_INVALID; + } + + if (key != 0 && key != prevkey) { + prevkey = key; if (key >= 0 && key <= 0xFF && isalpha(key)) select2[v] = tolower(key); - else if ((key == Common::KEYCODE_LCTRL) || (key == Common::KEYCODE_RCTRL)) - select2[v] = '\164'; - else if (key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9) + else if ((key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9) || key == Common::KEYCODE_SPACE) select2[v] = key; - else if (key == Common::KEYCODE_SPACE) - select2[v] = '\167'; else if (key == Common::KEYCODE_ESCAPE) break; else if (key == Common::KEYCODE_RETURN) { diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp index de82899462..6f88a58fbb 100644 --- a/engines/drascula/saveload.cpp +++ b/engines/drascula/saveload.cpp @@ -59,6 +59,8 @@ bool DrasculaEngine::saveLoadScreen() { select[0] = 0; + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + for (;;) { y = 27; copyBackground(0, 0, 0, 0, 320, 200, bgSurface, screenSurface); @@ -140,8 +142,10 @@ bool DrasculaEngine::saveLoadScreen() { } if (mouseX > 125 && mouseY > 123 && mouseX < 199 && mouseY < 149 && selectionMade == 1) { - if (!loadGame(file)) + if (!loadGame(file)) { + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); return false; + } break; } else if (mouseX > 208 && mouseY > 123 && mouseX < 282 && mouseY < 149 && selectionMade == 1) { saveGame(file); @@ -172,6 +176,8 @@ bool DrasculaEngine::saveLoadScreen() { loadPic(roomNumber, bgSurface, HALF_PAL); selectionMade = 0; + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); + return true; } diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp index 840d6c7cb5..2eb40e2e30 100644 --- a/engines/drascula/sound.cpp +++ b/engines/drascula/sound.cpp @@ -37,23 +37,25 @@ void DrasculaEngine::updateVolume(Audio::Mixer::SoundType soundType, int prevVol } void DrasculaEngine::volumeControls() { - int masterVolume, voiceVolume, musicVolume; - copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface); updateScreen(73, 63, 73, 63, 177, 97, screenSurface); - masterVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16) * 4); - voiceVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16) * 4); - musicVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16) * 4); - for (;;) { + int masterVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16), 0, 15); + int voiceVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16), 0, 15); + int musicVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16), 0, 15); + + int masterVolumeY = 72 + 61 - masterVolume * 4; + int voiceVolumeY = 72 + 61 - voiceVolume * 4; + int musicVolumeY = 72 + 61 - musicVolume * 4; + updateRoom(); copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface); - copyBackground(183, 56, 82, masterVolume, 39, 2 + ((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16) * 4), tableSurface, screenSurface); - copyBackground(183, 56, 138, voiceVolume, 39, 2 + ((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16) * 4), tableSurface, screenSurface); - copyBackground(183, 56, 194, musicVolume, 39, 2 + ((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16) * 4), tableSurface, screenSurface); + copyBackground(183, 56, 82, masterVolumeY, 39, 2 + masterVolume * 4, tableSurface, screenSurface); + copyBackground(183, 56, 138, voiceVolumeY, 39, 2 + voiceVolume * 4, tableSurface, screenSurface); + copyBackground(183, 56, 194, musicVolumeY, 39, 2 + musicVolume * 4, tableSurface, screenSurface); setCursorTable(); @@ -68,18 +70,15 @@ void DrasculaEngine::volumeControls() { if (leftMouseButton == 1) { delay(100); if (mouseX > 80 && mouseX < 121) { - updateVolume(Audio::Mixer::kPlainSoundType, mouseY); - masterVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16) * 4); + updateVolume(Audio::Mixer::kPlainSoundType, masterVolumeY); } if (mouseX > 136 && mouseX < 178) { - updateVolume(Audio::Mixer::kSFXSoundType, mouseY); - voiceVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16) * 4); + updateVolume(Audio::Mixer::kSFXSoundType, voiceVolumeY); } if (mouseX > 192 && mouseX < 233) { - updateVolume(Audio::Mixer::kMusicSoundType, mouseY); - musicVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16) * 4); + updateVolume(Audio::Mixer::kMusicSoundType, musicVolumeY); } } diff --git a/engines/gob/mult_v2.cpp b/engines/gob/mult_v2.cpp index 20a81174e5..6bd4ddb625 100644 --- a/engines/gob/mult_v2.cpp +++ b/engines/gob/mult_v2.cpp @@ -510,10 +510,11 @@ void Mult_v2::playMultInit() { if (!_animSurf) { int16 width, height; - for (int i = 0; i < _objCount; i++) { - delete _objects[i].pPosX; - delete _objects[i].pPosY; - } + if (_objects) + for (int i = 0; i < _objCount; i++) { + delete _objects[i].pPosX; + delete _objects[i].pPosY; + } delete[] _objects; diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 2d592069d2..0c6eb29665 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -56,6 +56,7 @@ namespace { #define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA2) #define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_KYRA2) #define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, Kyra::GI_KYRA2) +#define KYRA2_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, false, Kyra::GI_KYRA2) #define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, Kyra::GI_KYRA2) #define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, Kyra::GI_KYRA2) #define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA2) @@ -66,8 +67,14 @@ namespace { #define KYRA3_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, true, false, Kyra::GI_KYRA3) #define LOL_CD_FLAGS FLAGS(false, false, true, false, false, false, Kyra::GI_LOL) +#define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_LOL) +#define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_LOL) +#define LOL_PC98_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_LOL) +#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, true, false, false, Kyra::GI_LOL) +#define LOL_DEMO_FLAGS FLAGS(true, false, false, false, false, false, Kyra::GI_KYRA2) const KYRAGameDescription adGameDescs[] = { + /* disable these targets until they get supported { { "kyra1", @@ -79,6 +86,7 @@ const KYRAGameDescription adGameDescs[] = { }, KYRA1_FLOPPY_CMP_FLAGS }, + { { "kyra1", @@ -90,6 +98,8 @@ const KYRAGameDescription adGameDescs[] = { }, KYRA1_FLOPPY_CMP_FLAGS }, + */ + { { "kyra1", @@ -210,7 +220,7 @@ const KYRAGameDescription adGameDescs[] = { { // FM-Towns version { "kyra1", - 0, + "CD", { { "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 }, { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 }, @@ -218,14 +228,14 @@ const KYRAGameDescription adGameDescs[] = { }, Common::EN_ANY, Common::kPlatformFMTowns, - Common::ADGF_NO_FLAGS + Common::ADGF_CD }, KYRA1_TOWNS_FLAGS }, { { "kyra1", - 0, + "CD", { { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 }, { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 }, @@ -233,7 +243,7 @@ const KYRAGameDescription adGameDescs[] = { }, Common::JA_JPN, Common::kPlatformFMTowns, - Common::ADGF_NO_FLAGS + Common::ADGF_CD }, KYRA1_TOWNS_SJIS_FLAGS }, @@ -421,6 +431,77 @@ const KYRAGameDescription adGameDescs[] = { KYRA2_CD_FLAGS }, + // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3" + { // CD version + { + "kyra2", + "CD", + AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"), + Common::IT_ITA, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY) + }, + { + { + "kyra2", + "CD", + AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"), + Common::DE_DEU, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY) + }, + { + { + "kyra2", + "CD", + AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"), + Common::FR_FRA, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY) + }, + + { + { + "kyra2", + "CD", + AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"), + Common::IT_ITA, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY) + }, + + { + { + "kyra2", + "CD", + AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"), + Common::DE_DEU, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY) + }, + + { + { + "kyra2", + "CD", + AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"), + Common::FR_FRA, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY) + }, + { // Interactive Demo { "kyra2", @@ -457,11 +538,11 @@ const KYRAGameDescription adGameDescs[] = { KYRA2_CD_DEMO_FLAGS }, - { // Non-Interactive Demo + { // Non-Interactive Demos { "kyra2", "Demo", - AD_ENTRY1("GENERAL.PAK", "35825783e5b60755fd520360079f9c15"), + AD_ENTRY1("VOC.PAK", "ecb3561b63749158172bf21528cf5f45"), Common::EN_ANY, Common::kPlatformPC, Common::ADGF_DEMO @@ -472,44 +553,44 @@ const KYRAGameDescription adGameDescs[] = { { // FM-Towns { "kyra2", - 0, + "CD", AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"), Common::EN_ANY, Common::kPlatformFMTowns, - Common::ADGF_NO_FLAGS + Common::ADGF_CD }, KYRA2_TOWNS_FLAGS }, { { "kyra2", - 0, + "CD", AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"), Common::JA_JPN, Common::kPlatformFMTowns, - Common::ADGF_NO_FLAGS + Common::ADGF_CD }, KYRA2_TOWNS_SJIS_FLAGS }, { // PC-9821 { "kyra2", - 0, + "CD", AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"), Common::EN_ANY, Common::kPlatformPC98, - Common::ADGF_NO_FLAGS + Common::ADGF_CD }, KYRA2_TOWNS_FLAGS }, { { "kyra2", - 0, + "CD", AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"), Common::JA_JPN, Common::kPlatformPC98, - Common::ADGF_NO_FLAGS + Common::ADGF_CD }, KYRA2_TOWNS_SJIS_FLAGS }, @@ -610,6 +691,53 @@ const KYRAGameDescription adGameDescs[] = { KYRA3_CD_INS_FLAGS }, + // Mac version + { + { + "kyra3", + 0, + { + { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 }, + { "AUD.PAK", 0, 0, -1 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, + Common::kPlatformMacintosh, + Common::ADGF_DROPLANGUAGE + }, + KYRA3_CD_INS_FLAGS + }, + { + { + "kyra3", + 0, + { + { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 }, + { "AUD.PAK", 0, 0, -1 }, + { 0, 0, 0, 0 } + }, + Common::DE_DEU, + Common::kPlatformMacintosh, + Common::ADGF_DROPLANGUAGE + }, + KYRA3_CD_INS_FLAGS + }, + { + { + "kyra3", + 0, + { + { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 }, + { "AUD.PAK", 0, 0, -1 }, + { 0, 0, 0, 0 } + }, + Common::FR_FRA, + Common::kPlatformMacintosh, + Common::ADGF_DROPLANGUAGE + }, + KYRA3_CD_INS_FLAGS + }, + // Spanish fan translation, see fr#1994040 "KYRA3: Add support for Spanish fan translation" { { @@ -657,7 +785,7 @@ const KYRAGameDescription adGameDescs[] = { KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY) }, - // Itlian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3" + // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3" { { "kyra3", @@ -752,7 +880,134 @@ const KYRAGameDescription adGameDescs[] = { }, LOL_CD_FLAGS }, + + { + { + "lol", + "CD", + { + { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 }, + { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + LOL_CD_FLAGS + }, + + { + { + "lol", + "CD", + { + { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 }, + { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 }, + { 0, 0, 0, 0 } + }, + Common::DE_DEU, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + LOL_CD_FLAGS + }, + { + { + "lol", + "CD", + { + { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 }, + { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 }, + { 0, 0, 0, 0 } + }, + Common::FR_FRA, + Common::kPlatformPC, + Common::ADGF_DROPLANGUAGE | Common::ADGF_CD + }, + LOL_CD_FLAGS + }, + + { + { + "lol", + 0, + { + { "WESTWOOD.1", 0, "3c61cb7de5b2ec452f5851f5075207ee", -1 }, + { 0, 0, 0, 0 } + }, + Common::DE_DEU, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + LOL_FLOPPY_CMP_FLAGS + }, + + { + { + "lol", + "Extracted", + { + { "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 }, + { "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 }, + { 0, 0, 0, 0 } + }, + Common::DE_DEU, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + LOL_FLOPPY_FLAGS + }, + + /* disable these targets until they get supported + { + { + "lol", + 0, + { + { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 }, + { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, + Common::kPlatformPC98, + Common::ADGF_NO_FLAGS + }, + LOL_PC98_FLAGS + }, + + { + { + "lol", + 0, + { + { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 }, + { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 }, + { 0, 0, 0, 0 } + }, + Common::JA_JPN, + Common::kPlatformPC98, + Common::ADGF_NO_FLAGS + }, + LOL_PC98_SJIS_FLAGS + },*/ + + { + { + "lol", + "Demo", + { + { "GENERAL.PAK", 0, "e94863d86c4597a2d581d05481c152ba", -1 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + LOL_DEMO_FLAGS + }, + { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0) } }; diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp index 35b343fc25..47ce698e50 100644 --- a/engines/kyra/gui_lok.cpp +++ b/engines/kyra/gui_lok.cpp @@ -530,7 +530,7 @@ int GUI_LoK::resumeGame(Button *button) { void GUI_LoK::setupSavegames(Menu &menu, int num) { Common::InSaveFile *in; - static char savenames[5][31]; + static char savenames[5][35]; uint8 startSlot; assert(num <= 5); @@ -549,7 +549,8 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) { KyraEngine_v1::SaveHeader header; for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); i++) { if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header))) { - strncpy(savenames[i], header.description.c_str(), 31); + strncpy(savenames[i], header.description.c_str(), ARRAYSIZE(savenames[0])); + savenames[i][34] = 0; menu.item[i].itemString = savenames[i]; menu.item[i].enabled = 1; menu.item[i].saveSlot = _saveSlots[i + _savegameOffset]; @@ -673,7 +674,7 @@ void GUI_LoK::updateSavegameString() { length = strlen(_savegameName); if (_keyPressed.ascii > 31 && _keyPressed.ascii < 127) { - if (length < 31) { + if (length < ARRAYSIZE(_savegameName)-1) { _savegameName[length] = _keyPressed.ascii; _savegameName[length+1] = 0; redrawTextfield(); diff --git a/engines/kyra/gui_lok.h b/engines/kyra/gui_lok.h index 49081c7ae2..16b7ef9183 100644 --- a/engines/kyra/gui_lok.h +++ b/engines/kyra/gui_lok.h @@ -162,7 +162,7 @@ private: bool _menuRestoreScreen; uint8 _toplevelMenu; int _savegameOffset; - char _savegameName[31]; + char _savegameName[35]; const char *_specialSavegameString; Common::KeyState _keyPressed; int8 _mouseWheel; diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp index 2819c4f077..e4cec760fa 100644 --- a/engines/kyra/gui_v2.cpp +++ b/engines/kyra/gui_v2.cpp @@ -456,6 +456,7 @@ void GUI_v2::setupSavegameNames(Menu &menu, int num) { for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); ++i) { if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header)) != 0) { strncpy(getTableString(menu.item[i].itemId), header.description.c_str(), 80); + getTableString(menu.item[i].itemId)[79] = 0; menu.item[i].saveSlot = _saveSlots[i + _savegameOffset]; menu.item[i].enabled = true; delete in; diff --git a/engines/kyra/gui_v2.h b/engines/kyra/gui_v2.h index 161752627b..60b7f0ab86 100644 --- a/engines/kyra/gui_v2.h +++ b/engines/kyra/gui_v2.h @@ -188,7 +188,7 @@ protected: // save menu bool _noSaveProcess; int _saveSlot; - char _saveDescription[0x50]; + char _saveDescription[0x51]; int saveMenu(Button *caller); int clickSaveSlot(Button *caller); diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp index d3de621707..33a8af91f1 100644 --- a/engines/kyra/kyra_hof.cpp +++ b/engines/kyra/kyra_hof.cpp @@ -196,14 +196,18 @@ void KyraEngine_HoF::pauseEngineIntern(bool pause) { _seqWsaChatTimeout += pausedTime; _seqWsaChatFrameTimeout += pausedTime; - for (int x = 0; x < 10; x++) { - if (_activeText[x].duration != -1) - _activeText[x].startTime += pausedTime; + if (_activeText) { + for (int x = 0; x < 10; x++) { + if (_activeText[x].duration != -1) + _activeText[x].startTime += pausedTime; + } } - for (int x = 0; x < 8; x++) { - if (_activeWSA[x].flags != -1) - _activeWSA[x].nextFrame += pausedTime; + if (_activeWSA) { + for (int x = 0; x < 8; x++) { + if (_activeWSA[x].flags != -1) + _activeWSA[x].nextFrame += pausedTime; + } } _nextIdleAnim += pausedTime; @@ -251,7 +255,7 @@ int KyraEngine_HoF::init() { _abortIntroFlag = false; if (_sequenceStrings) { - for (int i = 0; i < 33; i++) + for (int i = 0; i < MIN(33, _sequenceStringsSize); i++) _sequenceStringsDuration[i] = (int) strlen(_sequenceStrings[i]) * 8; } @@ -278,7 +282,10 @@ int KyraEngine_HoF::go() { seq_showStarcraftLogo(); if (_flags.isDemo && !_flags.isTalkie) { - seq_playSequences(kSequenceDemoVirgin, kSequenceDemoFisher); + if (_flags.gameID == GI_LOL) + seq_playSequences(kSequenceLolDemoScene1, kSequenceLolDemoScene6); + else + seq_playSequences(kSequenceDemoVirgin, kSequenceDemoFisher); _menuChoice = 4; } else { seq_playSequences(kSequenceVirgin, kSequenceZanfaun); @@ -292,10 +299,12 @@ int KyraEngine_HoF::go() { if (_menuChoice != 4) { // load just the pak files needed for ingame _res->loadPakFile(StaticResource::staticDataFilename()); - if (_flags.platform == Common::kPlatformPC && _flags.isTalkie) - _res->loadFileList("FILEDATA.FDT"); - else + if (_flags.platform == Common::kPlatformPC && _flags.isTalkie) { + if (!_res->loadFileList("FILEDATA.FDT")) + error("couldn't load 'FILEDATA.FDT'"); + } else { _res->loadFileList(_ingamePakList, _ingamePakListSize); + } if (_flags.platform == Common::kPlatformPC98) _res->loadPakFile("AUDIO.PAK"); @@ -1501,15 +1510,19 @@ void KyraEngine_HoF::openTalkFile(int newFile) { _oldTalkFile = -1; } - if (newFile == 0) { + if (newFile == 0) strcpy(talkFilename, "ANYTALK.TLK"); - _res->loadPakFile(talkFilename); - } else { + else sprintf(talkFilename, "CH%dVOC.TLK", newFile); - _res->loadPakFile(talkFilename); - } _oldTalkFile = newFile; + + if (!_res->loadPakFile(talkFilename)) { + if (speechEnabled()) { + warning("Couldn't load file '%s' falling back to text only mode", talkFilename); + _configVoice = 0; + } + } } void KyraEngine_HoF::snd_playVoiceFile(int id) { @@ -1545,7 +1558,8 @@ void KyraEngine_HoF::playVoice(int high, int low) { if (!_flags.isTalkie) return; int vocFile = high * 10000 + low * 10; - snd_playVoiceFile(vocFile); + if (speechEnabled()) + snd_playVoiceFile(vocFile); } void KyraEngine_HoF::snd_playSoundEffect(int track, int volume) { diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h index 866dd55d16..f6e887c648 100644 --- a/engines/kyra/kyra_hof.h +++ b/engines/kyra/kyra_hof.h @@ -97,6 +97,20 @@ enum kNestedSequencesDemo { kSequenceDemoDig }; +enum kSequencesLolDemo { + kSequenceLolDemoScene1 = 0, + kSequenceLolDemoText1, + kSequenceLolDemoScene2, + kSequenceLolDemoText2, + kSequenceLolDemoScene3, + kSequenceLolDemoText3, + kSequenceLolDemoScene4, + kSequenceLolDemoText4, + kSequenceLolDemoScene5, + kSequenceLolDemoText5, + kSequenceLolDemoScene6 +}; + class WSAMovie_v2; class KyraEngine_HoF; class TextDisplayer_HoF; @@ -242,6 +256,14 @@ protected: int seq_demoBail(WSAMovie_v2 *wsaObj, int x, int y, int frm); int seq_demoDig(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoScene1(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoScene2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoScene3(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoScene4(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoText5(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm); + void seq_sequenceCommand(int command); void seq_loadNestedSequence(int wsaNum, int seqNum); void seq_nestedSequenceFrame(int command, int wsaNum); @@ -264,7 +286,7 @@ protected: WSAMovie_v2 * wsa, int firstframe, int lastframe, int wsaXpos, int wsaYpos); void seq_finaleActorScreen(); void seq_displayScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData = 0, const char *const *specialData = 0); - void seq_scrollPage(); + void seq_scrollPage(int bottom, int top); void seq_showStarcraftLogo(); void seq_init(); @@ -280,8 +302,7 @@ protected: static const int8 _dosTrackMap[]; static const int _dosTrackMapSize; - const AudioDataStruct *_soundData; - + AudioDataStruct _soundData[3]; protected: // game initialization void startup(); diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h index cb3062847e..1def95ddbf 100644 --- a/engines/kyra/kyra_lok.h +++ b/engines/kyra/kyra_lok.h @@ -633,7 +633,7 @@ protected: int _soundFilesIntroSize; const int32 *_cdaTrackTable; int _cdaTrackTableSize; - const AudioDataStruct * _soundData; + AudioDataStruct _soundData[3]; // positions of the inventory static const uint16 _itemPosX[]; diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp index a4e5b58364..a61253199b 100644 --- a/engines/kyra/kyra_mr.cpp +++ b/engines/kyra/kyra_mr.cpp @@ -508,7 +508,8 @@ void KyraEngine_MR::snd_playVoiceFile(int file) { char filename[16]; snprintf(filename, 16, "%.08u", (uint)file); - _voiceSoundChannel = _soundDigital->playSound(filename, 0xFE, Audio::Mixer::kSpeechSoundType, 255); + if (speechEnabled()) + _voiceSoundChannel = _soundDigital->playSound(filename, 0xFE, Audio::Mixer::kSpeechSoundType, 255); } bool KyraEngine_MR::snd_voiceIsPlaying() { @@ -802,7 +803,12 @@ void KyraEngine_MR::openTalkFile(int file) { } _currentTalkFile = file; - _res->loadPakFile(talkFilename); + if (!_res->loadPakFile(talkFilename)) { + if (speechEnabled()) { + warning("Couldn't load file '%s' falling back to text only mode", talkFilename); + _configVoice = 0; + } + } } #pragma mark - diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp index 85c03dc1bb..bc46d8e1f5 100644 --- a/engines/kyra/kyra_v1.cpp +++ b/engines/kyra/kyra_v1.cpp @@ -114,7 +114,7 @@ int KyraEngine_v1::init() { _sound = new SoundTownsPC98_v2(this, _mixer); } else if (_flags.platform == Common::kPlatformPC98) { if (_flags.gameID == GI_KYRA1) - _sound = new SoundTowns/*SoundPC98*/(this, _mixer); + _sound = new SoundPC98(this, _mixer); else _sound = new SoundTownsPC98_v2(this, _mixer); } else if (midiDriver == MD_ADLIB) { @@ -152,6 +152,16 @@ int KyraEngine_v1::init() { _res = new Resource(this); assert(_res); _res->reset(); + + if (_flags.isDemo) { + // HACK: check whether this is the HOF demo or the LOL demo. + // The LOL demo needs to be detected and run as KyraEngine_HoF, + // but the static resource loader and the sequence player will + // need correct IDs. + if (_res->exists("scene1.cps")) + _flags.gameID = GI_LOL; + } + _staticres = new StaticResource(this); assert(_staticres); if (!_staticres->init()) diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index 50cabc421e..738a3fc8ec 100644 --- a/engines/kyra/kyra_v1.h +++ b/engines/kyra/kyra_v1.h @@ -34,8 +34,8 @@ #include "kyra/script.h" namespace Common { -class InSaveFile; -class OutSaveFile; +class SeekableReadStream; +class WriteStream; } // end of namespace Common class KyraMetaEngine; @@ -70,9 +70,9 @@ enum { struct AudioDataStruct { const char * const *_fileList; - const int _fileListLen; - const void * const _cdaTracks; - const int _cdaNumTracks; + int _fileListLen; + const void * _cdaTracks; + int _cdaNumTracks; }; // TODO: this is just the start of makeing the debug output of the kyra engine a bit more useable @@ -299,10 +299,10 @@ protected: kRSHEIoError = 3 }; - static kReadSaveHeaderError readSaveHeader(Common::InSaveFile *file, SaveHeader &header); + static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, SaveHeader &header); - Common::InSaveFile *openSaveForReading(const char *filename, SaveHeader &header); - Common::OutSaveFile *openSaveForWriting(const char *filename, const char *saveName) const; + Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header); + Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName) const; }; } // End of namespace Kyra diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp index ef1121baa0..6624dd91ea 100644 --- a/engines/kyra/lol.cpp +++ b/engines/kyra/lol.cpp @@ -87,6 +87,9 @@ int LoLEngine::init() { _screen->setAnimBlockPtr(10000); _screen->setScreenDim(0); + if (!_sound->init()) + error("Couldn't init sound"); + return 0; } @@ -256,11 +259,14 @@ void LoLEngine::setupPrologueData(bool load) { "xxx/intro9.pak" }; - char filename[32]; + char filepath[32]; + char *filename = filepath; for (uint i = 0; i < ARRAYSIZE(fileList); ++i) { strcpy(filename, fileList[i]); memcpy(filename, _languageExt[_lang], 3); - + if (!_flags.isTalkie) + filename += 4; + if (load) { if (!_res->loadPakFile(filename)) error("Couldn't load file: '%s'", filename); diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp index 92818aafe1..5da6bb3873 100644 --- a/engines/kyra/resource.cpp +++ b/engines/kyra/resource.cpp @@ -55,12 +55,10 @@ bool Resource::reset() { if (!dir.exists() || !dir.isDirectory()) error("invalid game path '%s'", dir.getPath().c_str()); - if (_vm->game() != GI_LOL) { - if (!loadPakFile(StaticResource::staticDataFilename()) || !StaticResource::checkKyraDat()) { - Common::String errorMessage = "You're missing the '" + StaticResource::staticDataFilename() + "' file or it got corrupted, (re)get it from the ScummVM website"; - _vm->GUIErrorMessage(errorMessage); - error(errorMessage.c_str()); - } + if (!loadPakFile(StaticResource::staticDataFilename()) || !StaticResource::checkKyraDat()) { + Common::String errorMessage = "You're missing the '" + StaticResource::staticDataFilename() + "' file or it got corrupted, (re)get it from the ScummVM website"; + _vm->GUIErrorMessage(errorMessage); + error(errorMessage.c_str()); } if (_vm->game() == GI_KYRA1) { @@ -91,17 +89,23 @@ bool Resource::reset() { return true; } else if (_vm->game() == GI_KYRA3) { - if (_vm->gameFlags().useInstallerPackage) - loadPakFile("WESTWOOD.001"); + if (_vm->gameFlags().useInstallerPackage) { + if (!loadPakFile("WESTWOOD.001")) + error("couldn't load file: 'WESTWOOD.001'"); + } // Add default file directories Common::File::addDefaultDirectory(ConfMan.get("path") + "malcolm"); Common::File::addDefaultDirectory(ConfMan.get("path") + "MALCOLM"); - loadFileList("FILEDATA.FDT"); + if (!loadFileList("FILEDATA.FDT")) + error("couldn't load file: 'FILEDATA.FDT'"); return true; } else if (_vm->game() == GI_LOL) { + if (_vm->gameFlags().useInstallerPackage) + tryLoadCompFiles(); + return true; } @@ -180,8 +184,14 @@ bool Resource::loadPakFile(const Common::String &filename) { for (ResArchiveLoader::FileList::iterator i = files.begin(); i != files.end(); ++i) { iter = _map.find(i->filename); if (iter == _map.end()) { + // We do an internal check for a file in gamepath with same filename to + // allow overwriting files inside archives with plain files inside the + // game directory + checkFile(i->filename); + // A new file entry, so we just insert it into the file map. - _map[i->filename] = i->entry; + if (_map.find(i->filename) == _map.end()) + _map[i->filename] = i->entry; } else if (!iter->_value.parent.empty()) { if (!iter->_value.parent.equalsIgnoreCase(filename)) { ResFileMap::iterator oldParent = _map.find(iter->_value.parent); @@ -349,7 +359,17 @@ bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) { Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) { CompFileMap::iterator compEntry; - if (Common::File::exists(file)) { + if ((compEntry = _compFiles.find(file)) != _compFiles.end()) + return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false); + + if (!isAccessable(file)) + return 0; + + ResFileMap::const_iterator iter = _map.find(file); + if (iter == _map.end()) + return 0; + + if (iter->_value.parent.empty()) { Common::File *stream = new Common::File(); if (!stream->open(file)) { delete stream; @@ -357,28 +377,15 @@ Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) error("Couldn't open file '%s'", file.c_str()); } return stream; - } else if ((compEntry = _compFiles.find(file)) != _compFiles.end()) { - return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false); } else { - if (!isAccessable(file)) - return 0; - - ResFileMap::const_iterator iter = _map.find(file); - if (iter == _map.end()) - return 0; - - if (!iter->_value.parent.empty()) { - Common::SeekableReadStream *parent = getFileStream(iter->_value.parent); - assert(parent); + Common::SeekableReadStream *parent = getFileStream(iter->_value.parent); + assert(parent); - ResFileMap::const_iterator parentIter = _map.find(iter->_value.parent); - const ResArchiveLoader *loader = getLoader(parentIter->_value.type); - assert(loader); + ResFileMap::const_iterator parentIter = _map.find(iter->_value.parent); + const ResArchiveLoader *loader = getLoader(parentIter->_value.type); + assert(loader); - return loader->loadFileFromArchive(file, parent, iter->_value); - } else { - error("Couldn't open file '%s'", file.c_str()); - } + return loader->loadFileFromArchive(file, parent, iter->_value); } return 0; @@ -410,7 +417,19 @@ void Resource::checkFile(const Common::String &file) { if (_map.find(file) == _map.end()) { CompFileMap::const_iterator iter; - if (Common::File::exists(file)) { + if ((iter = _compFiles.find(file)) != _compFiles.end()) { + ResFileEntry entry; + entry.parent = ""; + entry.size = iter->_value.size; + entry.mounted = false; + entry.preload = false; + entry.prot = false; + entry.type = ResFileEntry::kAutoDetect; + entry.offset = 0; + _map[file] = entry; + + detectFileTypes(); + } else if (Common::File::exists(file)) { Common::File temp; if (temp.open(file)) { ResFileEntry entry; @@ -426,18 +445,6 @@ void Resource::checkFile(const Common::String &file) { detectFileTypes(); } - } else if ((iter = _compFiles.find(file)) != _compFiles.end()) { - ResFileEntry entry; - entry.parent = ""; - entry.size = iter->_value.size; - entry.mounted = false; - entry.preload = false; - entry.prot = false; - entry.type = ResFileEntry::kAutoDetect; - entry.offset = 0; - _map[file] = entry; - - detectFileTypes(); } } } @@ -1256,10 +1263,17 @@ uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 ¶) { class CompLoaderInsHof : public CompArchiveLoader { public: - bool checkForFiles() const; - bool loadFile(CompFileMap &loadTo) const; + CompLoaderInsHof() { + _fileExtP = "%03d"; + _checkFile1 = "WESTWOOD.001"; + _checkFile2 = "WESTWOOD.002"; + _containerOffset = 6; + } -private: + virtual bool checkForFiles() const; + virtual bool loadFile(CompFileMap &loadTo) const; + +protected: struct Archive { Common::String filename; uint32 firstFile; @@ -1268,10 +1282,25 @@ private: uint32 endOffset; uint32 totalSize; }; + + const char *_fileExtP; + const char *_checkFile1; + const char *_checkFile2; + uint8 _containerOffset; +}; + +class CompLoaderInsLol : public CompLoaderInsHof { +public: + CompLoaderInsLol() { + _fileExtP = "%d"; + _checkFile1 = "WESTWOOD.1"; + _checkFile2 = "WESTWOOD.2"; + _containerOffset = 0; + } }; bool CompLoaderInsHof::checkForFiles() const { - return (Common::File::exists("WESTWOOD.001") && Common::File::exists("WESTWOOD.002")); + return (Common::File::exists(_checkFile1) && Common::File::exists(_checkFile2)); } bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { @@ -1293,7 +1322,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { Common::List<Archive> archives; for (int8 currentFile = 1; currentFile; currentFile++) { - sprintf(filenameExt, "%03d", currentFile); + sprintf(filenameExt, _fileExtP, currentFile); filenameTemp = filenameBase + Common::String(filenameExt); if (!tmpFile.open(filenameTemp)) { @@ -1309,9 +1338,9 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { if (startFile) { size -= 4; if (fileId == currentFile) { - size -= 6; - pos += 6; - tmpFile.seek(6, SEEK_CUR); + size -= _containerOffset; + pos += _containerOffset; + tmpFile.seek(_containerOffset, SEEK_CUR); } else { size = size + 1 - pos; } @@ -1355,6 +1384,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { uint8 *outbuffer = 0; uint32 inPart1 = 0; uint32 inPart2 = 0; + uint8 compressionType = 0; Common::String entryStr; pos = 0; @@ -1366,7 +1396,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { for (Common::List<Archive>::iterator a = archives.begin(); a != archives.end(); ++a) { startFile = true; for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) { - sprintf(filenameExt, "%03d", i); + sprintf(filenameExt, _fileExtP, i); filenameTemp = a->filename + Common::String(filenameExt); if (!tmpFile.open(filenameTemp)) { @@ -1389,7 +1419,12 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { tmpFile.seek(1); tmpFile.read(inbuffer + inPart1, inPart2); inPart2 = 0; - exp.process(outbuffer, inbuffer, outsize, insize); + + if (compressionType > 0) + exp.process(outbuffer, inbuffer, outsize, insize); + else + memcpy(outbuffer, inbuffer, outsize); + delete[] inbuffer; inbuffer = 0; newEntry.data = outbuffer; @@ -1418,7 +1453,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { } } - sprintf(filenameExt, "%03d", i + 1); + sprintf(filenameExt, _fileExtP, i + 1); filenameTemp = a->filename + Common::String(filenameExt); Common::File tmpFile2; @@ -1434,8 +1469,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { uint32 id = READ_LE_UINT32(hdr); if (id == 0x04034B50) { - if (hdr[8] != 8) - error("compression type not implemented"); + compressionType = hdr[8]; insize = READ_LE_UINT32(hdr + 18); outsize = READ_LE_UINT32(hdr + 22); @@ -1463,7 +1497,12 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const { } else { tmpFile.read(inbuffer, insize); inPart2 = 0; - exp.process(outbuffer, inbuffer, outsize, insize); + + if (compressionType > 0) + exp.process(outbuffer, inbuffer, outsize, insize); + else + memcpy(outbuffer, inbuffer, outsize); + delete[] inbuffer; inbuffer = 0; newEntry.data = outbuffer; @@ -1497,6 +1536,7 @@ void Resource::initializeLoaders() { _loaders.push_back(LoaderList::value_type(new ResLoaderTlk())); _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsHof())); + _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsLol())); } const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const { diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp index 22f934ba69..31c5e15fa6 100644 --- a/engines/kyra/saveload.cpp +++ b/engines/kyra/saveload.cpp @@ -37,7 +37,7 @@ namespace Kyra { -KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSaveFile *in, SaveHeader &header) { +KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header) { uint32 type = in->readUint32BE(); header.originalSave = false; header.oldHeader = false; @@ -95,6 +95,9 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSave if (header.version <= 8) { char buffer[31]; in->read(buffer, 31); + // WORKAROUND: Old savegames could contain a missing termination 0 at the + // end so we manually add it. + buffer[30] = 0; header.description = buffer; } else { header.description = ""; @@ -108,10 +111,10 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSave return (in->ioFailed() ? kRSHEIoError : kRSHENoError); } -Common::InSaveFile *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) { +Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) { debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForReading('%s', -)", filename); - Common::InSaveFile *in = 0; + Common::SeekableReadStream *in = 0; if (!(in = _saveFileMan->openForLoading(filename))) return 0; @@ -159,12 +162,12 @@ Common::InSaveFile *KyraEngine_v1::openSaveForReading(const char *filename, Save return in; } -Common::OutSaveFile *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const { +Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const { debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForWriting('%s', '%s')", filename, saveName); if (_quitFlag) return 0; - Common::OutSaveFile *out = 0; + Common::WriteStream *out = 0; if (!(out = _saveFileMan->openForSaving(filename))) { warning("Can't create file '%s', game not saved", filename); return 0; @@ -209,7 +212,7 @@ bool KyraEngine_v1::saveFileLoadable(int slot) { return false; SaveHeader header; - Common::InSaveFile *in = openSaveForReading(getSavegameFilename(slot), header); + Common::SeekableReadStream *in = openSaveForReading(getSavegameFilename(slot), header); if (in) { delete in; diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp index e4a3a5c54e..53c0cb5380 100644 --- a/engines/kyra/scene_mr.cpp +++ b/engines/kyra/scene_mr.cpp @@ -378,10 +378,11 @@ void KyraEngine_MR::loadSceneMsc() { _screen->loadBitmap(filename, 5, 5, 0, true); // HACK - uint8 data[320*200]; + uint8 *data = new uint8[320*200]; _screen->copyRegionToBuffer(5, 0, 0, 320, 200, data); _screen->clearPage(5); _screen->copyBlockToPage(5, 0, _maskPageMinY, 320, height, data); + delete[] data; musicUpdate(0); } diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 74f7bc6de9..0cde066cc0 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -413,7 +413,9 @@ void Screen::fadePalette(const uint8 *palData, int delay, const UpdateFunctor *u void Screen::getFadeParams(const uint8 *palette, int delay, int &delayInc, int &diff) { debugC(9, kDebugLevelScreen, "Screen::getFadeParams(%p, %d, %p, %p)", (const void *)palette, delay, (const void *)&delayInc, (const void *)&diff); uint8 maxDiff = 0; - for (int i = 0; i < 768; ++i) { + + const int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256) * 3; + for (int i = 0; i < colors; ++i) { diff = ABS(palette[i] - _screenPalette[i]); maxDiff = MAX<uint8>(maxDiff, diff); } @@ -438,7 +440,8 @@ int Screen::fadePalStep(const uint8 *palette, int diff) { memcpy(fadePal, _screenPalette, 768); bool needRefresh = false; - for (int i = 0; i < 768; ++i) { + const int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256) * 3; + for (int i = 0; i < colors; ++i) { int c1 = palette[i]; int c2 = fadePal[i]; if (c1 != c2) { @@ -476,7 +479,7 @@ void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) { void Screen::setScreenPalette(const uint8 *palData) { debugC(9, kDebugLevelScreen, "Screen::setScreenPalette(%p)", (const void *)palData); - int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256); + const int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256); if (palData != _screenPalette) memcpy(_screenPalette, palData, colors*3); diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp index 7993fb8de6..9b215b2c03 100644 --- a/engines/kyra/script_tim.cpp +++ b/engines/kyra/script_tim.cpp @@ -559,15 +559,7 @@ int TIMInterpreter::cmd_playVocFile(const uint16 *param) { int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) { const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1))); - - static char * fileList[] = { 0 }; - fileList[0] = _audioFilename; - static AudioDataStruct audioList = { fileList, 1, 0, 0 }; - - strncpy(_audioFilename, file, sizeof(_audioFilename)); - - _vm->sound()->setSoundList(&audioList); - _vm->sound()->loadSoundFile(0); + _vm->sound()->loadSoundFile(file); return 1; } diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp index 169c319347..635db3629c 100644 --- a/engines/kyra/sequences_hof.cpp +++ b/engines/kyra/sequences_hof.cpp @@ -50,7 +50,7 @@ void KyraEngine_HoF::seq_playSequences(int startSeq, int endSeq) { _sound->setSoundList(&_soundData[(startSeq > kSequenceZanfaun) ? kMusicFinale : kMusicIntro]); _sound->loadSoundFile(0); - _screen->_charWidth = -2; + _screen->_charWidth = (_flags.gameID == GI_LOL) ? 0 : -2; memset(_activeWSA, 0, sizeof(ActiveWSA) * 8); for (int i = 0; i < 8; ++i) @@ -300,8 +300,8 @@ void KyraEngine_HoF::seq_playSequences(int startSeq, int endSeq) { _eventList.clear(); seqNum = kSequenceFirates; } - } else if (seqNum == kSequenceDemoFisher && !(_abortIntroFlag || skipFlag())) { - seqNum = kSequenceDemoVirgin; + } else if (seqNum == endSeq && !(_abortIntroFlag || skipFlag())) { + seqNum = 0; } if (_menuChoice) { @@ -1722,7 +1722,7 @@ int KyraEngine_HoF::seq_demoFisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) { _seqScrollTextCounter = 0; } - seq_scrollPage(); + seq_scrollPage(24, 144); _seqFrameCounter++; if (_seqFrameCounter < 0x256 || _seqFrameCounter > 0x31c) { if (_seqFrameCounter < 0x174 || _seqFrameCounter > 0x1d7) { @@ -1740,7 +1740,7 @@ int KyraEngine_HoF::seq_demoFisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) { } } else { - seq_scrollPage(); + seq_scrollPage(24, 144); } return 0; } @@ -1796,6 +1796,182 @@ int KyraEngine_HoF::seq_demoDig(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } +int KyraEngine_HoF::seq_lolDemoScene1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint8 *tmpPal = _screen->getPalette(2); + + if (!(_seqFrameCounter % 100)) { + if (_seqFrameCounter == 0) { + _sound->haltTrack(); + _sound->playTrack(6); + } + memcpy(tmpPal, _screen->getPalette(0), 0x300); + for (int i = 3; i < 0x300; i++) { + tmpPal[i] = ((int)tmpPal[i] * 120) / 64; + if (tmpPal[i] > 0x3f) + tmpPal[i] = 0x3f; + } + seq_playTalkText(_rnd.getRandomBit()); + _screen->setScreenPalette(tmpPal); + _screen->updateScreen(); + delay(8); + } else { + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->updateScreen(); + if (_seqFrameCounter == 40) + seq_playTalkText(3); + } + + _seqFrameCounter++; + return frm; +} + +int KyraEngine_HoF::seq_lolDemoScene2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_seqFrameCounter - 17) { + case 0: + _seqFrameDelay = 8; + break; + case 3: + case 6: + case 9: + seq_playTalkText(8); + break; + case 15: + seq_playTalkText(9); + break; + case 18: + seq_playTalkText(2); + break; + default: + break; + } + _seqFrameCounter++; + return frm; +} + +int KyraEngine_HoF::seq_lolDemoScene3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (_seqFrameCounter == 1) + seq_playTalkText(6); + else if (frm == 26) + seq_playTalkText(7); + + _seqFrameCounter++; + return frm; +} + +int KyraEngine_HoF::seq_lolDemoScene4(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_seqFrameCounter) { + case 11: + case 14: + case 17: + case 20: + seq_playTalkText(8); + break; + case 22: + seq_playTalkText(11); + break; + case 24: + seq_playTalkText(8); + break; + case 30: + seq_playTalkText(15); + break; + case 34: + seq_playTalkText(14); + break; + case 38: + seq_playTalkText(13); + break; + case 42: + seq_playTalkText(12); + break; + default: + break; + } + + _seqFrameCounter++; + return frm; +} + +int KyraEngine_HoF::seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_seqFrameCounter++) { + case 0: + case 4: + case 6: + case 8: + case 10: + case 14: + case 16: + case 18: + case 20: + case 22: + case 24: + case 26: + case 28: + case 30: + seq_playTalkText(15); + break; + case 32: + seq_playTalkText(16); + break; + case 42: + seq_playTalkText(6); + break; + default: + break; + } + return frm; +} + +int KyraEngine_HoF::seq_lolDemoText5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (_seqFrameCounter++ == 100) + seq_playTalkText(5); + return frm; +} + +int KyraEngine_HoF::seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + while (_seqScrollTextCounter < 0x122) { + _seqEndTime = _system->getMillis() + 6 * _tickLength; + if (!_seqFrameCounter) { + _screen->loadBitmap("adtext.cps", 4, 4, 0); + _screen->loadBitmap("adtext2.cps", 6, 6, 0); + _screen->copyPageMemory(6, 0, 4, 64000, 1024); + _screen->copyPageMemory(6, 1023, 6, 0, 64000); + _seqScrollTextCounter = 0; + } + + if (_seqFrameCounter % 175) { + _screen->setScreenPalette(_screen->getPalette(0)); + } else { + uint8 *tmpPal = _screen->getPalette(2); + memcpy(tmpPal, _screen->getPalette(0), 0x300); + for (int i = 3; i < 0x300; i++) { + tmpPal[i] = ((int)tmpPal[i] * 120) / 64; + if (tmpPal[i] > 0x3f) + tmpPal[i] = 0x3f; + } + seq_playTalkText(_rnd.getRandomBit()); + _screen->setScreenPalette(tmpPal); + _screen->updateScreen(); + delay(8); + } + + if (_seqFrameCounter == 40 || _seqFrameCounter == 80 || _seqFrameCounter == 150 || _seqFrameCounter == 300) + seq_playTalkText(3); + + _screen->copyPage(12, 2); + seq_scrollPage(70, 130); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _seqFrameCounter++; + if (_seqFrameCounter < 128 || _seqFrameCounter > 207) + _seqScrollTextCounter++; + delayUntil(_seqEndTime); + } + _screen->copyPage(2, 12); + + return 0; +} + uint32 KyraEngine_HoF::seq_activeTextsTimeLeft() { uint32 res = 0; @@ -1892,16 +2068,14 @@ void KyraEngine_HoF::seq_sequenceCommand(int command) { switch (command) { case 0: memset(pal, 0, 0x300); - _screen->fadePalette(pal, 16); + _screen->fadePalette(pal, 36); memcpy (_screen->getPalette(0), pal, 0x300); memcpy (_screen->getPalette(1), pal, 0x300); break; case 1: memset(pal, 0x3F, 0x300); - //////////XXX - //////////Unused anyway (at least by fm-towns intro/outro) - + seq_playTalkText(_rnd.getRandomBit()); _screen->fadePalette(pal, 16); memcpy (_screen->getPalette(0), pal, 0x300); memcpy (_screen->getPalette(1), pal, 0x300); @@ -2575,32 +2749,34 @@ void KyraEngine_HoF::seq_displayScrollText(uint8 *data, const ScreenDim *d, int delete[] textData; } -void KyraEngine_HoF::seq_scrollPage() { +void KyraEngine_HoF::seq_scrollPage(int bottom, int top) { int dstY, dstH, srcH; static const ScreenDim d = { 0x00, 0x00, 0x28, 0x320, 0xFF, 0xFE, 0x00, 0x00 }; - if (_seqScrollTextCounter - 143 < 0) { - dstY = 144 - _seqScrollTextCounter; + if (_seqScrollTextCounter - (top - 1) < 0) { + dstY = top - _seqScrollTextCounter; dstH = _seqScrollTextCounter; srcH = 0; } else { dstY = 0; - srcH = _seqScrollTextCounter - 144; - dstH = (400 - srcH <= 144) ? 400 - srcH : 144; + srcH = _seqScrollTextCounter - top; + dstH = (400 - srcH <= top) ? 400 - srcH : top; } if (dstH > 0) { - for (int i = 0; i < 4; i++) { - const ItemAnimData_v1 *def = &_demoAnimData[i]; - ActiveItemAnim *a = &_activeItemAnim[i]; - - _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4); - _screen->drawShape(4, getShapePtr(def->itemIndex + def->frames[a->currentFrame]), 12, def->y - 8, 0, 0); - if(_seqFrameCounter % 2 == 0) - a->currentFrame = ++a->currentFrame % 20; + if (_demoAnimData) { + for (int i = 0; i < 4; i++) { + const ItemAnimData_v1 *def = &_demoAnimData[i]; + ActiveItemAnim *a = &_activeItemAnim[i]; + + _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4); + _screen->drawShape(4, getShapePtr(def->itemIndex + def->frames[a->currentFrame]), 12, def->y - 8, 0, 0); + if(_seqFrameCounter % 2 == 0) + a->currentFrame = ++a->currentFrame % 20; + } } - _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + 24, 320, dstH, &d); + _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + bottom, 320, dstH, &d); } } @@ -2656,6 +2832,10 @@ void KyraEngine_HoF::seq_init() { _res->loadFileList(_sequencePakList, _sequencePakListSize); int numShp = -1; + + if (_flags.gameID == GI_LOL) + return; + if (_flags.isDemo && !_flags.isTalkie) { _demoAnimData = _staticres->loadShapeAnimData_v1(k2SeqplayShapeAnimData, _itemAnimDataSize); uint8 *shp = _res->fileData("icons.shp", 0); diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp index c8749dc06b..073639e4ca 100644 --- a/engines/kyra/sound.cpp +++ b/engines/kyra/sound.cpp @@ -104,6 +104,11 @@ int32 Sound::voicePlay(const char *file, bool isSfx) { fileSize = 0; } + if (!audioStream) { + warning("Couldn't load sound file '%s'", file); + return 0; + } + _soundChannels[h].file = file; _mixer->playInputStream(isSfx ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType, &_soundChannels[h].channelHandle, audioStream); @@ -323,7 +328,17 @@ struct DeleterArray { void SoundMidiPC::loadSoundFile(uint file) { Common::StackLock lock(_mutex); - Common::String filename = fileListEntry(file); + internalLoadFile(fileListEntry(file)); +} + +void SoundMidiPC::loadSoundFile(Common::String file) { + Common::StackLock lock(_mutex); + + internalLoadFile(file); +} + +void SoundMidiPC::internalLoadFile(Common::String file) { + Common::String filename = file; filename += "."; filename += _useC55 ? "C55" : "XMI"; diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h index cebfdf491f..e5294eb15d 100644 --- a/engines/kyra/sound.h +++ b/engines/kyra/sound.h @@ -120,6 +120,12 @@ public: virtual void loadSoundFile(uint file) = 0; /** + * Load a sound file for playing music + * and sound effects from. + */ + virtual void loadSoundFile(Common::String file) = 0; + + /** * Plays the specified track. * * @param track track number @@ -215,8 +221,6 @@ protected: int _musicEnabled; bool _sfxEnabled; - int _currentTheme; - KyraEngine_v1 *_vm; Audio::Mixer *_mixer; @@ -260,6 +264,7 @@ public: void process(); void loadSoundFile(uint file); + void loadSoundFile(Common::String file); void playTrack(uint8 track); void haltTrack(); @@ -269,6 +274,8 @@ public: void beginFadeOut(); private: + void internalLoadFile(Common::String file); + void play(uint8 track); void unk1(); @@ -280,7 +287,8 @@ private: uint8 _trackEntries[500]; uint8 *_soundDataPtr; int _sfxPlayingSound; - uint _soundFileLoaded; + + Common::String _soundFileLoaded; uint8 _sfxPriority; uint8 _sfxFourthByteOfSong; @@ -316,6 +324,7 @@ public: void updateVolumeSettings(); void loadSoundFile(uint file); + void loadSoundFile(Common::String file); void playTrack(uint8 track); void haltTrack(); @@ -343,6 +352,7 @@ public: bool isMT32() const { return _nativeMT32; } private: + void internalLoadFile(Common::String file); void updateChannelVolume(uint8 vol); static void onTimer(void *data); @@ -397,6 +407,7 @@ public: void process(); void loadSoundFile(uint file); + void loadSoundFile(Common::String) {} void playTrack(uint8 track); void haltTrack(); @@ -454,6 +465,7 @@ public: void process() {} void loadSoundFile(uint file) {} + void loadSoundFile(Common::String) {} void playTrack(uint8 track); void haltTrack(); @@ -480,6 +492,7 @@ public: void process(); void loadSoundFile(uint file) {} + void loadSoundFile(Common::String) {} void playTrack(uint8 track); void haltTrack(); @@ -513,6 +526,7 @@ public: void setSoundList(const AudioDataStruct * list) { _music->setSoundList(list); _sfx->setSoundList(list); } bool hasSoundFile(uint file) const { return _music->hasSoundFile(file) && _sfx->hasSoundFile(file); } void loadSoundFile(uint file) { _music->loadSoundFile(file); _sfx->loadSoundFile(file); } + void loadSoundFile(Common::String file) { _music->loadSoundFile(file); _sfx->loadSoundFile(file); } void playTrack(uint8 track) { _music->playTrack(track); } void haltTrack() { _music->haltTrack(); } diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp index 0ceb288b8a..62551d2b09 100644 --- a/engines/kyra/sound_adlib.cpp +++ b/engines/kyra/sound_adlib.cpp @@ -2225,7 +2225,7 @@ SoundAdlibPC::SoundAdlibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer) assert(_driver); _sfxPlayingSound = -1; - _soundFileLoaded = (uint)-1; + _soundFileLoaded.clear(); if (_v2) { // TODO: Figure out if Kyra 2 uses sound triggers at all. @@ -2269,7 +2269,7 @@ void SoundAdlibPC::playTrack(uint8 track) { // sync for each loop. To avoid that, we declare that all four // of the song channels have to jump "in sync". - if (track == 4 && scumm_stricmp(fileListEntry(_soundFileLoaded), "KYRA1B") == 0) + if (track == 4 && _soundFileLoaded.equalsIgnoreCase("KYRA1B.ADL")) _driver->setSyncJumpMask(0x000F); else _driver->setSyncJumpMask(0); @@ -2348,6 +2348,15 @@ void SoundAdlibPC::beginFadeOut() { } void SoundAdlibPC::loadSoundFile(uint file) { + internalLoadFile(fileListEntry(file)); +} + +void SoundAdlibPC::loadSoundFile(Common::String file) { + internalLoadFile(file); +} + +void SoundAdlibPC::internalLoadFile(Common::String file) { + file += ".ADL"; if (_soundFileLoaded == file) return; @@ -2356,12 +2365,9 @@ void SoundAdlibPC::loadSoundFile(uint file) { uint8 *file_data = 0; uint32 file_size = 0; - char filename[25]; - sprintf(filename, "%s.ADL", fileListEntry(file)); - - file_data = _vm->resource()->fileData(filename, &file_size); + file_data = _vm->resource()->fileData(file.c_str(), &file_size); if (!file_data) { - warning("Couldn't find music file: '%s'", filename); + warning("Couldn't find music file: '%s'", file.c_str()); return; } diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 0f2b916c9d..ec1962a58f 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -2363,7 +2363,7 @@ TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) : _numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false), _numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) { setTempo(84); - _baserate = (double)getRate() / 10368.0; + _baserate = (486202500.0 / (double)getRate()) / 10368.0; } TownsPC98_OpnDriver::~TownsPC98_OpnDriver() { diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 9a4b40902e..bb63c24c36 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -43,7 +43,7 @@ namespace Kyra { -#define RESFILE_VERSION 28 +#define RESFILE_VERSION 31 bool StaticResource::checkKyraDat() { Common::File kyraDat; @@ -279,6 +279,16 @@ bool StaticResource::init() { { 0, 0, 0 } }; + static const FilenameTable lolStaticRes[] = { + // Demo Sequence Player + { k2SeqplayPakFiles, kStringList, "S_PAKFILES.TXT" }, + { k2SeqplayStrings, kLanguageList, "S_STRINGS." }, + { k2SeqplaySfxFiles, kStringList, "S_SFXFILES.TXT" }, + { k2SeqplaySeqData, k2SeqData, "S_DATA.SEQ" }, + { k2SeqplayIntroTracks, kStringList, "S_INTRO.TRA" }, + { 0, 0, 0 } + }; + if (_vm->game() == GI_KYRA1) { _builtIn = 0; _filenameTable = kyra1StaticRes; @@ -289,7 +299,10 @@ bool StaticResource::init() { _builtIn = 0; _filenameTable = kyra3StaticRes; } else if (_vm->game() == GI_LOL) { - return true; + if (!_vm->gameFlags().isDemo) + return true; + _builtIn = 0; + _filenameTable = lolStaticRes; } else { error("StaticResource: Unknown game ID"); } @@ -920,6 +933,8 @@ const char *StaticResource::getFilename(const char *name) { filename += ".K2"; else if (_vm->gameFlags().gameID == GI_KYRA3) filename += ".K3"; + else if (_vm->gameFlags().gameID == GI_LOL) + filename += ".LOL"; if (_vm->gameFlags().isTalkie && _vm->gameFlags().gameID != GI_KYRA3) filename += ".CD"; @@ -1037,38 +1052,51 @@ void KyraEngine_LoK::initStaticResource() { } // audio data tables -#if 0 static const char *tIntro98[] = { "intro%d.dat" }; static const char *tIngame98[] = { "kyram%d.dat" }; -#endif - - static const AudioDataStruct soundData_PC[] = { - { _soundFilesIntro, _soundFilesIntroSize, 0, 0 }, - { _soundFiles, _soundFilesSize, 0, 0 }, - { 0, 0, 0, 0} - }; - - static const AudioDataStruct soundData_TOWNS[] = { - { _soundFiles, _soundFilesSize, _cdaTrackTable, _cdaTrackTableSize }, - { _soundFiles, _soundFilesSize, _cdaTrackTable, _cdaTrackTableSize }, - { 0, 0, 0, 0} - }; - -#if 0 - static const AudioDataStruct soundData_PC98[] = { - { tIntro98, 1, 0, 0 }, - { tIngame98, 1, 0, 0 }, - { 0, 0, 0, 0} - }; -#endif - - if (_flags.platform == Common::kPlatformPC) - _soundData = soundData_PC; - else if (_flags.platform == Common::kPlatformFMTowns) - _soundData = soundData_TOWNS; - else if (_flags.platform == Common::kPlatformPC98) - _soundData = soundData_TOWNS/*soundData_PC98*/; + if (_flags.platform == Common::kPlatformPC) { + _soundData[0]._fileList = _soundFilesIntro; + _soundData[0]._fileListLen = _soundFilesIntroSize; + _soundData[0]._cdaTracks = 0; + _soundData[0]._cdaNumTracks = 0; + _soundData[1]._fileList = _soundFiles; + _soundData[1]._fileListLen = _soundFilesSize; + _soundData[1]._cdaTracks = 0; + _soundData[1]._cdaNumTracks = 0; + _soundData[2]._fileList = 0; + _soundData[2]._fileListLen = 0; + _soundData[2]._cdaTracks = 0; + _soundData[2]._cdaNumTracks = 0; + } else if (_flags.platform == Common::kPlatformFMTowns) { + _soundData[0]._fileList = _soundFiles; + _soundData[0]._fileListLen = _soundFilesSize; + _soundData[0]._cdaTracks = _cdaTrackTable; + _soundData[0]._cdaNumTracks = _cdaTrackTableSize; + _soundData[1]._fileList = _soundFiles; + _soundData[1]._fileListLen = _soundFilesSize; + _soundData[1]._cdaTracks = _cdaTrackTable; + _soundData[1]._cdaNumTracks = _cdaTrackTableSize; + _soundData[2]._fileList = 0; + _soundData[2]._fileListLen = 0; + _soundData[2]._cdaTracks = 0; + _soundData[2]._cdaNumTracks = 0; + } else if (_flags.platform == Common::kPlatformPC98) { + _soundData[0]._fileList = tIntro98; + _soundData[0]._fileListLen = 1; + _soundData[0]._cdaTracks = 0; + _soundData[0]._cdaNumTracks = 0; + _soundData[1]._fileList = tIngame98; + _soundData[1]._fileListLen = 1; + _soundData[1]._cdaTracks = 0; + _soundData[1]._cdaNumTracks = 0; + _soundData[2]._fileList = 0; + _soundData[2]._fileListLen = 0; + _soundData[2]._cdaTracks = 0; + _soundData[2]._cdaNumTracks = 0; + } else { + memset(_soundData, 0, sizeof(_soundData)); + } } void KyraEngine_LoK::loadMouseShapes() { @@ -1266,38 +1294,50 @@ void KyraEngine_HoF::initStaticResource() { static const char *fmtMusicFileListFinale[] = { "finale%d.twn" }; static const char *fmtMusicFileListIngame[] = { "km%02d.twn" }; -#if 0 static const char *pc98MusicFileListIntro[] = { "intro%d.86" }; static const char *pc98MusicFileListFinale[] = { "finale%d.86" }; static const char *pc98MusicFileListIngame[] = { "km%02d.86" }; -#endif - - static const AudioDataStruct soundData_PC[] = { - { _musicFileListIntro, _musicFileListIntroSize, 0, 0 }, - { _musicFileListIngame, _musicFileListIngameSize, 0, 0}, - { _musicFileListFinale, _musicFileListIntroSize, 0, 0 } - }; - static const AudioDataStruct soundData_TOWNS[] = { - { fmtMusicFileListIntro, 1, _cdaTrackTableIntro, _cdaTrackTableIntroSize >> 1 }, - { fmtMusicFileListIngame, 1, _cdaTrackTableIngame, _cdaTrackTableIngameSize >> 1 }, - { fmtMusicFileListFinale, 1, _cdaTrackTableFinale, _cdaTrackTableFinaleSize >> 1 } - }; - -#if 0 - static const AudioDataStruct soundData_PC98[] = { - { pc98MusicFileListIntro, 1, 0, 0 }, - { pc98MusicFileListIngame, 1, 0, 0 }, - { pc98MusicFileListFinale, 1, 0, 0 } - }; -#endif - - if (_flags.platform == Common::kPlatformPC) - _soundData = soundData_PC; - else if (_flags.platform == Common::kPlatformFMTowns) - _soundData = soundData_TOWNS; - else if (_flags.platform == Common::kPlatformPC98) - _soundData = soundData_TOWNS/*soundData_PC98*/; + if (_flags.platform == Common::kPlatformPC) { + _soundData[0]._fileList = _musicFileListIntro; + _soundData[0]._fileListLen = _musicFileListIntroSize; + _soundData[0]._cdaTracks = 0; + _soundData[0]._cdaNumTracks = 0; + _soundData[1]._fileList = _musicFileListIngame; + _soundData[1]._fileListLen = _musicFileListIngameSize; + _soundData[1]._cdaTracks = 0; + _soundData[1]._cdaNumTracks = 0; + _soundData[2]._fileList = _musicFileListFinale; + _soundData[2]._fileListLen = _musicFileListIntroSize; + _soundData[2]._cdaTracks = 0; + _soundData[2]._cdaNumTracks = 0; + } else if (_flags.platform == Common::kPlatformFMTowns) { + _soundData[0]._fileList = fmtMusicFileListIntro; + _soundData[0]._fileListLen = 1; + _soundData[0]._cdaTracks = _cdaTrackTableIntro; + _soundData[0]._cdaNumTracks = _cdaTrackTableIntroSize >> 1; + _soundData[1]._fileList = fmtMusicFileListIngame; + _soundData[1]._fileListLen = 1; + _soundData[1]._cdaTracks = _cdaTrackTableIngame; + _soundData[1]._cdaNumTracks = _cdaTrackTableIngameSize >> 1; + _soundData[2]._fileList = fmtMusicFileListFinale; + _soundData[2]._fileListLen = 1; + _soundData[2]._cdaTracks = _cdaTrackTableFinale; + _soundData[2]._cdaNumTracks = _cdaTrackTableFinaleSize >> 1; + } else if (_flags.platform == Common::kPlatformPC98) { + _soundData[0]._fileList = pc98MusicFileListIntro; + _soundData[0]._fileListLen = 1; + _soundData[0]._cdaTracks = 0; + _soundData[0]._cdaNumTracks = 0; + _soundData[1]._fileList = pc98MusicFileListIngame; + _soundData[1]._fileListLen = 1; + _soundData[1]._cdaTracks = 0; + _soundData[1]._cdaNumTracks = 0; + _soundData[2]._fileList = pc98MusicFileListFinale; + _soundData[2]._fileListLen = 1; + _soundData[2]._cdaTracks = 0; + _soundData[2]._cdaNumTracks = 0; + } // setup sequence data _sequences = _staticres->loadHofSequenceData(k2SeqplaySeqData, tmpSize); @@ -1336,8 +1376,17 @@ void KyraEngine_HoF::initStaticResource() { &KyraEngine_HoF::seq_demoDig, 0 }; - _callbackS = (_flags.isDemo && !_flags.isTalkie) ? hofDemoSequenceCallbacks : hofSequenceCallbacks; - _callbackN = (_flags.isDemo && !_flags.isTalkie) ? hofDemoNestedSequenceCallbacks : hofNestedSequenceCallbacks; + static const SeqProc lolDemoSequenceCallbacks[] = { + &KyraEngine_HoF::seq_lolDemoScene1, 0, &KyraEngine_HoF::seq_lolDemoScene2, 0, + &KyraEngine_HoF::seq_lolDemoScene3, 0, &KyraEngine_HoF::seq_lolDemoScene4, 0, + &KyraEngine_HoF::seq_lolDemoScene5, &KyraEngine_HoF::seq_lolDemoText5, + &KyraEngine_HoF::seq_lolDemoScene6, 0 + }; + + static const SeqProc lolDemoNestedSequenceCallbacks[] = { 0 }; + + _callbackS = _flags.gameID == GI_LOL ? lolDemoSequenceCallbacks : ((_flags.isDemo && !_flags.isTalkie) ? hofDemoSequenceCallbacks : hofSequenceCallbacks); + _callbackN = _flags.gameID == GI_LOL ? lolDemoNestedSequenceCallbacks : ((_flags.isDemo && !_flags.isTalkie) ? hofDemoNestedSequenceCallbacks : hofNestedSequenceCallbacks); } void KyraEngine_MR::initStaticResource() { diff --git a/engines/parallaction/balloons.cpp b/engines/parallaction/balloons.cpp index 81b32adb15..222954ec3a 100644 --- a/engines/parallaction/balloons.cpp +++ b/engines/parallaction/balloons.cpp @@ -30,6 +30,183 @@ namespace Parallaction { +class WrappedLineFormatter { + +protected: + Common::String _line; + Font *_font; + uint16 _lines, _lineWidth; + + virtual void setup() = 0; + virtual void action() = 0; + virtual void end() = 0; + virtual Common::String expand(const Common::String &token) { return token; } + + void textAccum(const Common::String &token, uint16 width) { + if (token.empty()) { + return; + } + + _lineWidth += width; + _line += token; + } + + void textNewLine() { + _lines++; + _lineWidth = 0; + _line.clear(); + } + +public: + WrappedLineFormatter(Font *font) : _font(font) { } + virtual ~WrappedLineFormatter() { } + + virtual void calc(const char *text, uint16 maxwidth) { + setup(); + + _lineWidth = 0; + _line.clear(); + _lines = 0; + + Common::StringTokenizer tokenizer(text, " "); + Common::String token; + Common::String blank(" "); + + uint16 blankWidth = _font->getStringWidth(" "); + uint16 tokenWidth = 0; + + while (!tokenizer.empty()) { + token = tokenizer.nextToken(); + token = expand(token); + + if (token == '/') { + tokenWidth = 0; + action(); + textNewLine(); + } else { + // todo: expand '%' + tokenWidth = _font->getStringWidth(token.c_str()); + + if (_lineWidth == 0) { + textAccum(token, tokenWidth); + } else { + if (_lineWidth + blankWidth + tokenWidth <= maxwidth) { + textAccum(blank, blankWidth); + textAccum(token, tokenWidth); + } else { + action(); + textNewLine(); + textAccum(token, tokenWidth); + } + } + } + } + + end(); + } +}; + +class StringExtent_NS : public WrappedLineFormatter { + + uint _width, _height; + +protected: + virtual Common::String expand(const Common::String &token) { + if (token.compareToIgnoreCase("%p") == 0) { + return Common::String("/"); + } + + return token; + } + + virtual void setup() { + _width = _height = 0; + + _line.clear(); + _lines = 0; + _width = 0; + } + + virtual void action() { + if (_lineWidth > _width) { + _width = _lineWidth; + } + _height = _lines * _font->height(); + } + + virtual void end() { + action(); + } + +public: + StringExtent_NS(Font *font) : WrappedLineFormatter(font) { } + + uint width() const { return _width; } + uint height() const { return _height; } +}; + + +class StringWriter_NS : public WrappedLineFormatter { + + uint _width, _height; + byte _color; + + Graphics::Surface *_surf; + +protected: + virtual Common::String expand(const Common::String& token) { + if (token.compareToIgnoreCase("%p") == 0) { + Common::String t("......."); + for (int i = 0; _password[i]; i++) { + t.setChar(_password[i], i); + } + return Common::String("> ") + t; + } else + if (token.compareToIgnoreCase("%s") == 0) { + char buf[20]; + sprintf(buf, "%i", _score); + return Common::String(buf); + } + + return token; + } + + virtual void setup() { + } + + virtual void action() { + if (_line.empty()) { + return; + } + uint16 rx = 10; + uint16 ry = 4 + _lines * _font->height(); // y + + byte *dst = (byte*)_surf->getBasePtr(rx, ry); + _font->setColor(_color); + _font->drawString(dst, _surf->w, _line.c_str()); + } + + virtual void end() { + action(); + } + +public: + StringWriter_NS(Font *font) : WrappedLineFormatter(font) { } + + void write(const char *text, uint maxWidth, byte color, Graphics::Surface *surf) { + StringExtent_NS se(_font); + se.calc(text, maxWidth); + _width = se.width() + 10; + _height = se.height() + 20; + _color = color; + _surf = surf; + + calc(text, maxWidth); + } + +}; + + #define BALLOON_TRANSPARENT_COLOR_NS 2 #define BALLOON_TRANSPARENT_COLOR_BR 0 @@ -78,8 +255,6 @@ class BalloonManager_ns : public BalloonManager { uint _numBalloons; - void getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height); - void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth); int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness); Balloon *getBalloon(uint id); @@ -152,12 +327,16 @@ int BalloonManager_ns::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w int16 w, h; - getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); + StringExtent_NS se(_vm->_dialogueFont); + se.calc(text, MAX_BALLOON_WIDTH); + w = se.width() + 14; + h = se.height() + 20; int id = createBalloon(w+5, h, winding, 1); Balloon *balloon = &_intBalloons[id]; - drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + StringWriter_NS sw(_vm->_dialogueFont); + sw.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -172,12 +351,17 @@ int BalloonManager_ns::setDialogueBalloon(char *text, uint16 winding, byte textC int16 w, h; - getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); + StringExtent_NS se(_vm->_dialogueFont); + se.calc(text, MAX_BALLOON_WIDTH); + w = se.width() + 14; + h = se.height() + 20; + int id = createBalloon(w+5, h, winding, 1); Balloon *balloon = &_intBalloons[id]; - drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + StringWriter_NS sw(_vm->_dialogueFont); + sw.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -196,7 +380,9 @@ int BalloonManager_ns::setDialogueBalloon(char *text, uint16 winding, byte textC void BalloonManager_ns::setBalloonText(uint id, char *text, byte textColor) { Balloon *balloon = getBalloon(id); balloon->surface->fillRect(balloon->innerBox, 1); - drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + + StringWriter_NS sw(_vm->_dialogueFont); + sw.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface); } @@ -204,11 +390,15 @@ int BalloonManager_ns::setLocationBalloon(char *text, bool endGame) { int16 w, h; - getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); + StringExtent_NS se(_vm->_dialogueFont); + se.calc(text, MAX_BALLOON_WIDTH); + w = se.width() + 14; + h = se.height() + 20; int id = createBalloon(w+(endGame ? 5 : 10), h+5, -1, BALLOON_TRANSPARENT_COLOR_NS); Balloon *balloon = &_intBalloons[id]; - drawWrappedText(_vm->_dialogueFont, balloon->surface, text, 0, MAX_BALLOON_WIDTH); + StringWriter_NS sw(_vm->_dialogueFont); + sw.write(text, MAX_BALLOON_WIDTH, 0, balloon->surface); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -245,106 +435,99 @@ void BalloonManager_ns::freeBalloons() { _numBalloons = 0; } -void BalloonManager_ns::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth) { - uint16 lines = 0; - uint16 linewidth = 0; - uint16 rx = 10; - uint16 ry = 4; - uint16 blankWidth = font->getStringWidth(" "); - uint16 tokenWidth = 0; - char token[MAX_TOKEN_LEN]; - if (wrapwidth == -1) - wrapwidth = _vm->_screenWidth; - while (strlen(text) > 0) { - text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true); - if (!scumm_stricmp(token, "%p")) { - lines++; - rx = 10; - ry = 4 + lines*10; // y - strcpy(token, "> ......."); - strncpy(token+2, _password, strlen(_password)); - tokenWidth = font->getStringWidth(token); - } else { - tokenWidth = font->getStringWidth(token); - linewidth += tokenWidth; +class StringExtent_BR : public WrappedLineFormatter { - if (linewidth > wrapwidth) { - // wrap line - lines++; - rx = 10; // x - ry = 4 + lines*10; // y - linewidth = tokenWidth; - } + uint _width, _height; - if (!scumm_stricmp(token, "%s")) { - sprintf(token, "%d", _score); - } +protected: + virtual void setup() { + _width = _height = 0; - } - - _gfx->drawText(font, surf, rx, ry, token, color); + _line.clear(); + _lines = 0; + _width = 0; + } - rx += tokenWidth + blankWidth; - linewidth += blankWidth; + virtual void action() { + if (_lineWidth > _width) { + _width = _lineWidth; + } + _height = _lines * _font->height(); + } - text = Common::ltrim(text); + virtual void end() { + action(); } -} +public: + StringExtent_BR(Font *font) : WrappedLineFormatter(font) { } -void BalloonManager_ns::getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height) { + uint width() const { return _width; } + uint height() const { return _height; } +}; - uint16 lines = 0; - uint16 w = 0; - *width = 0; - uint16 blankWidth = font->getStringWidth(" "); - uint16 tokenWidth = 0; +class StringWriter_BR : public WrappedLineFormatter { - char token[MAX_TOKEN_LEN]; + uint _width, _height; + byte _color; + uint _x, _y; - while (strlen(text) != 0) { + Graphics::Surface *_surf; - text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true); - tokenWidth = font->getStringWidth(token); +protected: + StringWriter_BR(Font *font, byte color) : WrappedLineFormatter(font) { - w += tokenWidth; + } - if (!scumm_stricmp(token, "%p")) { - lines++; - } else { - if (w > maxwidth) { - w -= tokenWidth; - lines++; - if (w > *width) - *width = w; + virtual void setup() { + } - w = tokenWidth; - } + virtual void action() { + if (_line.empty()) { + return; } + uint16 rx = _x + (_surf->w - _lineWidth) / 2; + uint16 ry = _y + _lines * _font->height(); // y - w += blankWidth; - text = Common::ltrim(text); + byte *dst = (byte*)_surf->getBasePtr(rx, ry); + _font->setColor(_color); + _font->drawString(dst, _surf->w, _line.c_str()); } - if (*width < w) *width = w; - *width += 10; + virtual void end() { + action(); + } - *height = lines * 10 + 20; +public: + StringWriter_BR(Font *font) : WrappedLineFormatter(font) { } - return; -} + void write(const char *text, uint maxWidth, byte color, Graphics::Surface *surf) { + maxWidth = 216; + + StringExtent_BR se(_font); + se.calc(text, maxWidth); + _width = se.width() + 10; + _height = se.height() + 12; + _color = color; + _surf = surf; + + _x = 0; + _y = (_surf->h - _height) / 2; + calc(text, maxWidth); + } +}; @@ -366,29 +549,12 @@ class BalloonManager_br : public BalloonManager { Frames *_rightBalloon; void cacheAnims(); - void getStringExtent(Font *font, const char *text, uint16 maxwidth, int16* width, int16* height); void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth); int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness); Balloon *getBalloon(uint id); Graphics::Surface *expandBalloon(Frames *data, int frameNum); - void textSetupRendering(const Common::String &text, Graphics::Surface *dest, Font *font, byte color); - void textEmitCenteredLine(); - void textAccum(const Common::String &token, uint16 width); - void textNewLine(); - - Common::String _textLine; - Graphics::Surface *_textSurf; - Font *_textFont; - uint16 _textX, _textY; - byte _textColor; - uint16 _textLines, _textWidth; - - void extentSetup(Font *font, int16 *width, int16 *height); - void extentAction(); - - int16 *_extentWidth, *_extentHeight; - + StringWriter_BR _writer; public: BalloonManager_br(Disk *disk, Gfx *gfx); @@ -447,7 +613,7 @@ int BalloonManager_br::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w balloon->surface = expandBalloon(src, srcFrame); src->getRect(srcFrame, balloon->box); - drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + _writer.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -455,8 +621,6 @@ int BalloonManager_br::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w balloon->obj->y = y + balloon->box.top; balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR; - printf("balloon (%i, %i)\n", balloon->obj->x, balloon->obj->y); - _numBalloons++; return id; @@ -485,7 +649,7 @@ int BalloonManager_br::setDialogueBalloon(char *text, uint16 winding, byte textC balloon->surface = expandBalloon(src, srcFrame); src->getRect(srcFrame, balloon->box); - drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + _writer.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -556,155 +720,9 @@ void BalloonManager_br::cacheAnims() { } -void BalloonManager_br::extentSetup(Font *font, int16 *width, int16 *height) { - _extentWidth = width; - _extentHeight = height; - - _textLine.clear(); - _textLines = 0; - _textWidth = 0; - _textFont = font; -} - -void BalloonManager_br::extentAction() { - if (_textWidth > *_extentWidth) { - *_extentWidth = _textWidth; - } - *_extentHeight = _textLines * _textFont->height(); -} - -void BalloonManager_br::textSetupRendering(const Common::String &text, Graphics::Surface *dest, Font *font, byte color) { - uint16 maxWidth = 216; - - int16 w, h; - getStringExtent(font, text.c_str(), maxWidth, &w, &h); - - w += 10; - h += 12; - - _textLine.clear(); - _textSurf = dest; - _textFont = font; - _textX = 0; - _textY = (_textSurf->h - h) / 2; - _textColor = color; - _textLines = 0; - _textWidth = 0; -} - -void BalloonManager_br::textEmitCenteredLine() { - if (_textLine.empty()) { - return; - } - uint16 rx = _textX + (_textSurf->w - _textWidth) / 2; - uint16 ry = _textY + _textLines * _textFont->height(); // y - _gfx->drawText(_textFont, _textSurf, rx, ry, _textLine.c_str(), _textColor); -} - -void BalloonManager_br::textAccum(const Common::String &token, uint16 width) { - if (token.empty()) { - return; - } - - _textWidth += width; - _textLine += token; -} - -void BalloonManager_br::textNewLine() { - _textLines++; - _textWidth = 0; - _textLine.clear(); -} - - -// TODO: really, base this and getStringExtent on some kind of LineTokenizer, instead of -// repeating the algorithm and changing a couple of lines. -void BalloonManager_br::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapWidth) { - textSetupRendering(text, surf, font, color); - - wrapWidth = 216; - - Common::StringTokenizer tokenizer(text, " "); - Common::String token; - Common::String blank(" "); - - uint16 blankWidth = font->getStringWidth(" "); - uint16 tokenWidth = 0; - - while (!tokenizer.empty()) { - token = tokenizer.nextToken(); - - if (token == '/') { - tokenWidth = 0; - textEmitCenteredLine(); - textNewLine(); - } else { - // todo: expand '%' - tokenWidth = font->getStringWidth(token.c_str()); - - if (_textWidth == 0) { - textAccum(token, tokenWidth); - } else { - if (_textWidth + blankWidth + tokenWidth <= wrapWidth) { - textAccum(blank, blankWidth); - textAccum(token, tokenWidth); - } else { - textEmitCenteredLine(); - textNewLine(); - textAccum(token, tokenWidth); - } - } - } - } - - textEmitCenteredLine(); -} - - - -void BalloonManager_br::getStringExtent(Font *font, const char *text, uint16 maxwidth, int16* width, int16* height) { - extentSetup(font, width, height); - - Common::StringTokenizer tokenizer(text, " "); - Common::String token; - Common::String blank(" "); - - uint16 blankWidth = font->getStringWidth(" "); - uint16 tokenWidth = 0; - - while (!tokenizer.empty()) { - token = tokenizer.nextToken(); - - if (token == '/') { - tokenWidth = 0; - extentAction(); - textNewLine(); - } else { - // todo: expand '%' - tokenWidth = font->getStringWidth(token.c_str()); - - if (_textWidth == 0) { - textAccum(token, tokenWidth); - } else { - if (_textWidth + blankWidth + tokenWidth <= maxwidth) { - textAccum(blank, blankWidth); - textAccum(token, tokenWidth); - } else { - extentAction(); - textNewLine(); - textAccum(token, tokenWidth); - } - } - } - } - - extentAction(); -} - - - -BalloonManager_br::BalloonManager_br(Disk *disk, Gfx *gfx) : _numBalloons(0), _disk(disk), _gfx(gfx), _leftBalloon(0), _rightBalloon(0) { +BalloonManager_br::BalloonManager_br(Disk *disk, Gfx *gfx) : _numBalloons(0), _disk(disk), _gfx(gfx), + _leftBalloon(0), _rightBalloon(0), _writer(_vm->_dialogueFont) { } BalloonManager_br::~BalloonManager_br() { diff --git a/engines/parallaction/callables_br.cpp b/engines/parallaction/callables_br.cpp index 628ba0c1f1..a4aec8e93e 100644 --- a/engines/parallaction/callables_br.cpp +++ b/engines/parallaction/callables_br.cpp @@ -41,7 +41,7 @@ void Parallaction_br::_c_ferrcycle(void*) { } void Parallaction_br::_c_lipsinc(void*) { - warning("Parallaction_br::_c_lipsinc() not yet implemented"); + warning("Unexpected lipsinc routine call! Please notify the team!"); } void Parallaction_br::_c_albcycle(void*) { diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp index 761e11dc7d..0f89ca22d1 100644 --- a/engines/parallaction/callables_ns.cpp +++ b/engines/parallaction/callables_ns.cpp @@ -211,21 +211,17 @@ void Parallaction_ns::_c_moveSarc(void *parm) { } } - _introSarcData1 = _introSarcData3 - _moveSarcZone1->_left; - a->_z = _introSarcData3; - a->_frame = _moveSarcZone1->_top - (_introSarcData1 / 20); - _introSarcData3 = _moveSarcZone1->_left; + _introSarcData1 = _introSarcData3 - _moveSarcZone1->getX(); + a->setZ(_introSarcData3); + a->setF(_moveSarcZone1->getY() - (_introSarcData1 / 20)); + _introSarcData3 = _moveSarcZone1->getX(); if (_introSarcData1 > 0) { - a->_left = _introSarcData1 / 2; + a->setX(_introSarcData1 / 2); + a->setY(2); } else { - a->_left = -_introSarcData1 / 2; - } - - if (_introSarcData1 > 0) { - a->_top = 2; - } else { - a->_top = -2; + a->setX(-_introSarcData1 / 2); + a->setY(-2); } return; @@ -236,11 +232,11 @@ void Parallaction_ns::_c_moveSarc(void *parm) { _moveSarcZone1->translate(_introSarcData1, -_introSarcData1 / 20); _moveSarcZone0->translate(_introSarcData1, -_introSarcData1 / 20); - if (_moveSarcZones[0]->_left == 35 && - _moveSarcZones[1]->_left == 68 && - _moveSarcZones[2]->_left == 101 && - _moveSarcZones[3]->_left == 134 && - _moveSarcZones[4]->_left == 167) { + if (_moveSarcZones[0]->getX() == 35 && + _moveSarcZones[1]->getX() == 68 && + _moveSarcZones[2]->getX() == 101 && + _moveSarcZones[3]->getX() == 134 && + _moveSarcZones[4]->getX() == 167) { a = findAnimation("finito"); @@ -261,7 +257,7 @@ void Parallaction_ns::_c_contaFoglie(void *parm) { if (num_foglie != 6) return; - _commandFlags |= 0x1000; + _globalFlags |= 0x1000; return; } @@ -482,8 +478,8 @@ void Parallaction_ns::_c_sketch(void *parm) { Graphics::drawLine(oldx, oldy, newx, newy, 0, zeroMask, &_gfx->_backgroundInfo); - _rightHandAnim->_left = newx; - _rightHandAnim->_top = newy - 20; + _rightHandAnim->setX(newx); + _rightHandAnim->setY(newy - 20); index++; @@ -496,10 +492,10 @@ void Parallaction_ns::_c_sketch(void *parm) { void Parallaction_ns::_c_shade(void *parm) { Common::Rect r( - _rightHandAnim->_left - 36, - _rightHandAnim->_top - 36, - _rightHandAnim->_left, - _rightHandAnim->_top + _rightHandAnim->getX() - 36, + _rightHandAnim->getY() - 36, + _rightHandAnim->getX(), + _rightHandAnim->getY() ); uint16 _di = r.left/4 + r.top * _gfx->_backgroundInfo->mask.internalWidth; diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index f57976594e..0ff38913f7 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -101,14 +101,14 @@ bool Debugger::Cmd_Locations(int argc, const char **argv) { bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) { - uint32 flags = _commandFlags; + uint32 flags = _globalFlags; DebugPrintf("+------------------------------+---------+\n" "| flag name | value |\n" "+------------------------------+---------+\n"); - for (uint i = 0; i < _vm->_globalTable->count(); i++) { + for (uint i = 0; i < _vm->_globalFlagsNames->count(); i++) { const char *value = ((flags & (1 << i)) == 0) ? "OFF" : "ON"; - DebugPrintf("|%-30s| %-6s|\n", _vm->_globalTable->item(i), value); + DebugPrintf("|%-30s| %-6s|\n", _vm->_globalFlagsNames->item(i), value); } DebugPrintf("+------------------------------+---------+\n"); @@ -157,7 +157,7 @@ bool Debugger::Cmd_Zones(int argc, const char **argv) { "+--------------------+---+---+---+---+--------+--------+\n"); for ( ; b != e; b++) { ZonePtr z = *b; - DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", z->_name, z->_left, z->_top, z->_right, z->_bottom, z->_type, z->_flags ); + DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", z->_name, z->getX(), z->getY(), z->getX() + z->width(), z->getY() + z->height(), z->_type, z->_flags ); } DebugPrintf("+--------------------+---+---+---+---+--------+--------+\n"); @@ -175,7 +175,7 @@ bool Debugger::Cmd_Animations(int argc, const char **argv) { "+--------------------+---+---+---+---+--------+--------+\n"); for ( ; b != e; b++) { AnimationPtr a = *b; - DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", a->_name, a->_left, a->_top, a->_z, a->_frame, a->_type, a->_flags ); + DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", a->_name, a->getX(), a->getY(), a->getZ(), a->getF(), a->_type, a->_flags ); } DebugPrintf("+--------------------+---+---+---+---+--------+--------+\n"); @@ -187,19 +187,19 @@ bool Debugger::Cmd_GfxObjects(int argc, const char **argv) { const char *objType[] = { "DOOR", "GET", "ANIM" }; - DebugPrintf("+--------------------+-----+-----+-----+-----+--------+--------+\n" - "| name | x | y | z | f | type | visi |\n" - "+--------------------+-----+-----+-----+-----+--------+--------+\n"); + DebugPrintf("+--------------------+-----+-----+-----+-------+-----+--------+--------+\n" + "| name | x | y | z | layer | f | type | visi |\n" + "+--------------------+-----+-----+-----+-------+-----+--------+--------+\n"); GfxObjList::iterator b = _vm->_gfx->_gfxobjList.begin(); GfxObjList::iterator e = _vm->_gfx->_gfxobjList.end(); for ( ; b != e; b++) { GfxObj *obj = *b; - DebugPrintf("|%-20s|%5i|%5i|%5i|%5i|%8s|%8x|\n", obj->getName(), obj->x, obj->y, obj->z, obj->frame, objType[obj->type], obj->isVisible() ); + DebugPrintf("|%-20s|%5i|%5i|%5i|%7i|%5i|%8s|%8x|\n", obj->getName(), obj->x, obj->y, obj->z, obj->layer, obj->frame, objType[obj->type], obj->isVisible() ); } - DebugPrintf("+--------------------+-----+-----+-----+-----+--------+--------+\n"); + DebugPrintf("+--------------------+-----+-----+-----+-------+-----+--------+--------+\n"); return true; } diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index 21584a0525..205d702aa0 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -168,7 +168,7 @@ bool DialogueManager::displayAnswer(uint16 i) { uint32 flags = _vm->getLocationFlags(); if (a->_yesFlags & kFlagsGlobal) - flags = _commandFlags | kFlagsGlobal; + flags = _globalFlags | kFlagsGlobal; // display suitable answers if (((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags)) { diff --git a/engines/parallaction/disk.h b/engines/parallaction/disk.h index 341229a649..2923f239d4 100644 --- a/engines/parallaction/disk.h +++ b/engines/parallaction/disk.h @@ -69,6 +69,7 @@ public: virtual Table* loadTable(const char* name) = 0; virtual Common::SeekableReadStream* loadMusic(const char* name) = 0; virtual Common::ReadStream* loadSound(const char* name) = 0; + virtual void loadMask(const char *name, MaskBuffer &buffer) { } }; @@ -248,6 +249,7 @@ public: Table* loadTable(const char* name); Common::SeekableReadStream* loadMusic(const char* name); Common::ReadStream* loadSound(const char* name); + void loadMask(const char *name, MaskBuffer &buffer); }; class DosDemo_br : public DosDisk_br { diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index cd57ec8822..f52567bea2 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -357,6 +357,29 @@ void DosDisk_br::loadSlide(BackgroundInfo& info, const char *name) { return; } +void DosDisk_br::loadMask(const char *name, MaskBuffer &buffer) { + if (!name) { + return; + } + + Common::String filepath; + FilesystemNode node; + Common::File stream; + + filepath = Common::String(name) + ".msk"; + node = _mskDir.getChild(filepath); + if (!node.exists()) { + errorFileNotFound(_mskDir, filepath); + } + stream.open(node); + + // NOTE: info.width and info.height are only valid if the background graphics + // have already been loaded + buffer.bigEndian = false; + stream.read(buffer.data, buffer.size); + stream.close(); +} + void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) { debugC(5, kDebugDisk, "DosDisk_br::loadScenery"); @@ -733,8 +756,6 @@ Font* AmigaDisk_br::loadFont(const char* name) { while ((ch = stream.readByte()) != 0) fontFile += ch; stream.close(); - printf("fontDir = %s, fontFile = %s\n", fontDir.c_str(), fontFile.c_str()); - node = _fntDir.getChild(fontDir); if (!node.exists()) { errorFileNotFound(_fntDir, fontDir); diff --git a/engines/parallaction/exec.h b/engines/parallaction/exec.h index 22e75744f1..4239857ec0 100644 --- a/engines/parallaction/exec.h +++ b/engines/parallaction/exec.h @@ -84,8 +84,6 @@ class CommandExec_ns : public CommandExec { Parallaction_ns *_vm; protected: - void updateGetZone(ZonePtr z, bool visible); - DECLARE_UNQUALIFIED_COMMAND_OPCODE(invalid); DECLARE_UNQUALIFIED_COMMAND_OPCODE(set); DECLARE_UNQUALIFIED_COMMAND_OPCODE(clear); diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index 0b7400f0f7..fe7b1b2903 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -97,8 +97,9 @@ void Parallaction_br::setupSubtitles(char *s, char *s2, int y) { } else { _subtitle[1] = -1; } - +#if 0 // disabled because no references to lip sync has been found in the scripts _subtitleLipSync = 0; +#endif } void Parallaction_br::clearSubtitles() { @@ -122,48 +123,23 @@ DECLARE_COMMAND_OPCODE(location) { DECLARE_COMMAND_OPCODE(open) { warning("Parallaction_br::cmdOp_open command not yet implemented"); - _ctxt.cmd->u._zone->_flags &= ~kFlagsClosed; - if (_ctxt.cmd->u._zone->u.door->gfxobj) { - _vm->updateDoor(_ctxt.cmd->u._zone); - } + _vm->updateDoor(_ctxt.cmd->u._zone, false); } DECLARE_COMMAND_OPCODE(close) { warning("Parallaction_br::cmdOp_close not yet implemented"); - _ctxt.cmd->u._zone->_flags |= kFlagsClosed; - if (_ctxt.cmd->u._zone->u.door->gfxobj) { - _vm->updateDoor(_ctxt.cmd->u._zone); - } + _vm->updateDoor(_ctxt.cmd->u._zone, true); } DECLARE_COMMAND_OPCODE(on) { - CommandData *data = &_ctxt.cmd->u; - ZonePtr z = data->_zone; - - if (z) { - z->_flags |= kFlagsActive; - z->_flags &= ~kFlagsRemove; - - if ((z->_type & 0xFFFF) & kZoneGet) { - _vm->_gfx->showGfxObj(z->u.get->gfxobj, true); - } - } + _vm->showZone(_ctxt.cmd->u._zone, true); } DECLARE_COMMAND_OPCODE(off) { - CommandData *data = &_ctxt.cmd->u; - ZonePtr z = data->_zone; - - if (z) { - z->_flags |= kFlagsRemove; - - if ((z->_type & 0xFFFF) & kZoneGet) { - _vm->_gfx->showGfxObj(z->u.get->gfxobj, false); - } - } + _vm->showZone(_ctxt.cmd->u._zone, false); } @@ -335,42 +311,18 @@ DECLARE_COMMAND_OPCODE(offsave) { DECLARE_INSTRUCTION_OPCODE(on) { - InstructionPtr inst = *_ctxt.inst; - ZonePtr z = inst->_z; - - if (z) { - z->_flags |= kFlagsActive; - z->_flags &= ~kFlagsRemove; - - if ((z->_type & 0xFFFF) & kZoneGet) { - _vm->_gfx->showGfxObj(z->u.get->gfxobj, true); - } - } + _vm->showZone((*_ctxt.inst)->_z, true); } DECLARE_INSTRUCTION_OPCODE(off) { - InstructionPtr inst = *_ctxt.inst; - ZonePtr z = inst->_z; - - if (z) { - z->_flags |= kFlagsRemove; - - if ((z->_type & 0xFFFF) & kZoneGet) { - _vm->_gfx->showGfxObj(z->u.get->gfxobj, false); - } - } + _vm->showZone((*_ctxt.inst)->_z, false); } DECLARE_INSTRUCTION_OPCODE(set) { InstructionPtr inst = *_ctxt.inst; - - int16 rvalue = inst->_opB.getRValue(); - int16* lvalue = inst->_opA.getLValue(); - - *lvalue = rvalue; - + inst->_opA.setValue(inst->_opB.getValue()); } @@ -378,7 +330,7 @@ DECLARE_INSTRUCTION_OPCODE(set) { DECLARE_INSTRUCTION_OPCODE(inc) { InstructionPtr inst = *_ctxt.inst; - int16 rvalue = inst->_opB.getRValue(); + int16 rvalue = inst->_opB.getValue(); if (inst->_flags & kInstMod) { // mod int16 _bx = (rvalue > 0 ? rvalue : -rvalue); @@ -387,32 +339,30 @@ DECLARE_INSTRUCTION_OPCODE(inc) { rvalue = (rvalue > 0 ? 1 : -1); } - int16 *lvalue = inst->_opA.getLValue(); + int16 lvalue = inst->_opA.getValue(); switch (inst->_index) { case INST_INC: - *lvalue += rvalue; + lvalue += rvalue; break; case INST_DEC: - *lvalue -= rvalue; + lvalue -= rvalue; break; case INST_MUL: - *lvalue *= rvalue; + lvalue *= rvalue; break; case INST_DIV: - *lvalue /= rvalue; + lvalue /= rvalue; break; default: error("This should never happen. Report immediately"); } - if (inst->_opA._flags & kParaLocal) { - inst->_opA._local->wrap(); - } + inst->_opA.setValue(lvalue); } @@ -445,11 +395,7 @@ DECLARE_INSTRUCTION_OPCODE(move) { DECLARE_INSTRUCTION_OPCODE(color) { InstructionPtr inst = *_ctxt.inst; - - int16 entry = inst->_opB.getRValue(); - - _vm->_gfx->_palette.setEntry(entry, inst->_colors[0], inst->_colors[1], inst->_colors[2]); - + _vm->_gfx->_palette.setEntry(inst->_opB.getValue(), inst->_colors[0], inst->_colors[1], inst->_colors[2]); } diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp index 99a492863b..4ea7d601e0 100644 --- a/engines/parallaction/exec_ns.cpp +++ b/engines/parallaction/exec_ns.cpp @@ -80,7 +80,7 @@ DECLARE_INSTRUCTION_OPCODE(off) { DECLARE_INSTRUCTION_OPCODE(loop) { InstructionPtr inst = *_ctxt.inst; - _ctxt.program->_loopCounter = inst->_opB.getRValue(); + _ctxt.program->_loopCounter = inst->_opB.getValue(); _ctxt.program->_loopStart = _ctxt.ip; } @@ -93,7 +93,7 @@ DECLARE_INSTRUCTION_OPCODE(endloop) { DECLARE_INSTRUCTION_OPCODE(inc) { InstructionPtr inst = *_ctxt.inst; - int16 _si = inst->_opB.getRValue(); + int16 _si = inst->_opB.getValue(); if (inst->_flags & kInstMod) { // mod int16 _bx = (_si > 0 ? _si : -_si); @@ -102,29 +102,22 @@ DECLARE_INSTRUCTION_OPCODE(inc) { _si = (_si > 0 ? 1 : -1); } - int16* lvalue = inst->_opA.getLValue(); + int16 lvalue = inst->_opA.getValue(); if (inst->_index == INST_INC) { - *lvalue += _si; + lvalue += _si; } else { - *lvalue -= _si; + lvalue -= _si; } - if (inst->_opA._flags & kParaLocal) { - inst->_opA._local->wrap(); - } + inst->_opA.setValue(lvalue); } DECLARE_INSTRUCTION_OPCODE(set) { InstructionPtr inst = *_ctxt.inst; - - int16 _si = inst->_opB.getRValue(); - int16 *lvalue = inst->_opA.getLValue(); - - *lvalue = _si; - + inst->_opA.setValue(inst->_opB.getValue()); } @@ -133,10 +126,10 @@ DECLARE_INSTRUCTION_OPCODE(put) { Graphics::Surface v18; v18.w = inst->_a->width(); v18.h = inst->_a->height(); - v18.pixels = inst->_a->getFrameData(inst->_a->_frame); + v18.pixels = inst->_a->getFrameData(inst->_a->getF()); - int16 x = inst->_opA.getRValue(); - int16 y = inst->_opB.getRValue(); + int16 x = inst->_opA.getValue(); + int16 y = inst->_opB.getValue(); bool mask = (inst->_flags & kInstMaskedPut) == kInstMaskedPut; _vm->_gfx->patchBackground(v18, x, y, mask); @@ -176,8 +169,8 @@ DECLARE_INSTRUCTION_OPCODE(sound) { DECLARE_INSTRUCTION_OPCODE(move) { InstructionPtr inst = (*_ctxt.inst); - int16 x = inst->_opA.getRValue(); - int16 y = inst->_opB.getRValue(); + int16 x = inst->_opA.getValue(); + int16 y = inst->_opB.getValue(); _vm->_char.scheduleWalk(x, y); } @@ -203,7 +196,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (_ctxt.cmd->u._flags & kFlagsGlobal) { _ctxt.cmd->u._flags &= ~kFlagsGlobal; - _commandFlags |= _ctxt.cmd->u._flags; + _globalFlags |= _ctxt.cmd->u._flags; } else { _vm->setLocationFlags(_ctxt.cmd->u._flags); } @@ -213,7 +206,7 @@ DECLARE_COMMAND_OPCODE(set) { DECLARE_COMMAND_OPCODE(clear) { if (_ctxt.cmd->u._flags & kFlagsGlobal) { _ctxt.cmd->u._flags &= ~kFlagsGlobal; - _commandFlags &= ~_ctxt.cmd->u._flags; + _globalFlags &= ~_ctxt.cmd->u._flags; } else { _vm->clearLocationFlags(_ctxt.cmd->u._flags); } @@ -246,48 +239,47 @@ DECLARE_COMMAND_OPCODE(location) { DECLARE_COMMAND_OPCODE(open) { - _ctxt.cmd->u._zone->_flags &= ~kFlagsClosed; - if (_ctxt.cmd->u._zone->u.door->gfxobj) { - _vm->updateDoor(_ctxt.cmd->u._zone); - } + _vm->updateDoor(_ctxt.cmd->u._zone, false); } DECLARE_COMMAND_OPCODE(close) { - _ctxt.cmd->u._zone->_flags |= kFlagsClosed; - if (_ctxt.cmd->u._zone->u.door->gfxobj) { - _vm->updateDoor(_ctxt.cmd->u._zone); - } + _vm->updateDoor(_ctxt.cmd->u._zone, true); } -void CommandExec_ns::updateGetZone(ZonePtr z, bool visible) { +void Parallaction::showZone(ZonePtr z, bool visible) { if (!z) { return; } + if (visible) { + z->_flags &= ~kFlagsRemove; + z->_flags |= kFlagsActive; + } else { + z->_flags |= kFlagsRemove; + } + if ((z->_type & 0xFFFF) == kZoneGet) { - _vm->_gfx->showGfxObj(z->u.get->gfxobj, visible); + _gfx->showGfxObj(z->u.get->gfxobj, visible); + + GetData *data = z->u.get; + if (data->hasMask && _gfx->_backgroundInfo->hasMask) { + if (visible) { + _gfx->_backgroundInfo->mask.bltOr(data->gfxobj->x, data->gfxobj->y, data->_mask[0], 0, 0, data->_mask->w, data->_mask->h); + } else { + _gfx->_backgroundInfo->mask.bltCopy(data->gfxobj->x, data->gfxobj->y, data->_mask[1], 0, 0, data->_mask->w, data->_mask->h); + } + } } } DECLARE_COMMAND_OPCODE(on) { - ZonePtr z = _ctxt.cmd->u._zone; - - if (z) { - z->_flags &= ~kFlagsRemove; - z->_flags |= kFlagsActive; - updateGetZone(z, true); - } + _vm->showZone(_ctxt.cmd->u._zone, true); } DECLARE_COMMAND_OPCODE(off) { - ZonePtr z = _ctxt.cmd->u._zone; - - if (z) { - _ctxt.cmd->u._zone->_flags |= kFlagsRemove; - updateGetZone(z, false); - } + _vm->showZone(_ctxt.cmd->u._zone, false); } @@ -299,7 +291,7 @@ DECLARE_COMMAND_OPCODE(call) { DECLARE_COMMAND_OPCODE(toggle) { if (_ctxt.cmd->u._flags & kFlagsGlobal) { _ctxt.cmd->u._flags &= ~kFlagsGlobal; - _commandFlags ^= _ctxt.cmd->u._flags; + _globalFlags ^= _ctxt.cmd->u._flags; } else { _vm->toggleLocationFlags(_ctxt.cmd->u._flags); } @@ -345,23 +337,31 @@ void Parallaction_ns::drawAnimations() { if ((anim->_flags & kFlagsActive) && ((anim->_flags & kFlagsRemove) == 0)) { if (anim->_flags & kFlagsNoMasked) - layer = 3; - else - layer = _gfx->_backgroundInfo->getLayer(anim->_top + anim->height()); + layer = LAYER_FOREGROUND; + else { + if (getGameType() == GType_Nippon) { + // Layer in NS depends on where the animation is on the screen, for each animation. + layer = _gfx->_backgroundInfo->getLayer(anim->getFrameY() + anim->height()); + } else { + // Layer in BRA is calculated from Z value. For characters it is the same as NS, + // but other animations can have Z set from scripts independently from their + // position on the screen. + layer = _gfx->_backgroundInfo->getLayer(anim->getZ()); + } + } if (obj) { _gfx->showGfxObj(obj, true); - obj->frame = anim->_frame; - obj->x = anim->_left; - obj->y = anim->_top; - obj->z = anim->_z; + obj->frame = anim->getF(); + obj->x = anim->getX(); + obj->y = anim->getY(); + obj->z = anim->getZ(); obj->layer = layer; } } if (((anim->_flags & kFlagsActive) == 0) && (anim->_flags & kFlagsRemove)) { anim->_flags &= ~kFlagsRemove; - anim->_oldPos.x = -1000; } if ((anim->_flags & kFlagsActive) && (anim->_flags & kFlagsRemove)) { @@ -418,7 +418,7 @@ void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator AnimationPtr a = (*it)->_anim; if (a->_flags & kFlagsCharacter) - a->_z = a->_top + a->height(); + a->setZ(a->getFrameY() + a->height()); if ((a->_flags & kFlagsActing) == 0) continue; @@ -426,7 +426,7 @@ void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator runScript(*it, a); if (a->_flags & kFlagsCharacter) - a->_z = a->_top + a->height(); + a->setZ(a->getFrameY() + a->height()); } _modCounter++; @@ -448,7 +448,7 @@ void CommandExec::runList(CommandList::iterator first, CommandList::iterator las CommandPtr cmd = *first; if (cmd->_flagsOn & kFlagsGlobal) { - useFlags = _commandFlags | kFlagsGlobal; + useFlags = _globalFlags | kFlagsGlobal; useLocalFlags = false; } else { useFlags = _vm->getLocationFlags(); @@ -601,7 +601,7 @@ void Parallaction::runCommentFrame() { } -uint16 Parallaction::runZone(ZonePtr z) { +void Parallaction::runZone(ZonePtr z) { debugC(3, kDebugExec, "runZone (%s)", z->_name); uint16 subtype = z->_type & 0xFFFF; @@ -611,20 +611,15 @@ uint16 Parallaction::runZone(ZonePtr z) { case kZoneExamine: enterCommentMode(z); - return 0; + return; case kZoneGet: - if (z->_flags & kFlagsFixed) break; - if (pickupItem(z) != 0) { - return 1; - } - z->_flags |= kFlagsRemove; + pickupItem(z); break; case kZoneDoor: if (z->_flags & kFlagsLocked) break; - z->_flags ^= kFlagsClosed; - updateDoor(z); + updateDoor(z, !(z->_flags & kFlagsClosed)); break; case kZoneHear: @@ -633,23 +628,24 @@ uint16 Parallaction::runZone(ZonePtr z) { case kZoneSpeak: enterDialogueMode(z); - return 0; + return; } debugC(3, kDebugExec, "runZone completed"); _cmdExec->run(z->_commands, z); - return 0; + return; } // // ZONE TYPE: DOOR // -void Parallaction::updateDoor(ZonePtr z) { +void Parallaction::updateDoor(ZonePtr z, bool close) { + z->_flags = close ? (z->_flags |= kFlagsClosed) : (z->_flags &= ~kFlagsClosed); if (z->u.door->gfxobj) { - uint frame = (z->_flags & kFlagsClosed ? 0 : 1); + uint frame = (close ? 0 : 1); // z->u.door->gfxobj->setFrame(frame); z->u.door->gfxobj->frame = frame; } @@ -663,13 +659,17 @@ void Parallaction::updateDoor(ZonePtr z) { // ZONE TYPE: GET // -int16 Parallaction::pickupItem(ZonePtr z) { - int r = addInventoryItem(z->u.get->_icon); - if (r != -1) { - _gfx->showGfxObj(z->u.get->gfxobj, false); +bool Parallaction::pickupItem(ZonePtr z) { + if (z->_flags & kFlagsFixed) { + return false; + } + + int slot = addInventoryItem(z->u.get->_icon); + if (slot != -1) { + showZone(z, false); } - return (r == -1); + return (slot != -1); } @@ -688,7 +688,7 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { if (z->_flags & kFlagsRemove) continue; Common::Rect r; - z->getRect(r); + z->getBox(r); r.right++; // adjust border because Common::Rect doesn't include bottom-right edge r.bottom++; @@ -697,7 +697,7 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { if (!r.contains(_si, _di)) { // out of Zone, so look for special values - if ((z->_left == -2) || (z->_left == -3)) { + if ((z->getX() == -2) || (z->getX() == -3)) { // WORKAROUND: this huge condition is needed because we made TypeData a collection of structs // instead of an union. So, merge->_obj1 and get->_icon were just aliases in the original engine, @@ -716,15 +716,15 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { } } - if (z->_left != -1) + if (z->getX() != -1) continue; - if (_si < _char._ani->_left) + if (_si < _char._ani->getFrameX()) continue; - if (_si > (_char._ani->_left + _char._ani->width())) + if (_si > (_char._ani->getFrameX() + _char._ani->width())) continue; - if (_di < _char._ani->_top) + if (_di < _char._ani->getFrameY()) continue; - if (_di > (_char._ani->_top + _char._ani->height())) + if (_di > (_char._ani->getFrameY() + _char._ani->height())) continue; } @@ -746,8 +746,8 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { AnimationPtr a = *ait; _a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation - _e = ((_si >= a->_left + a->width()) || (_si <= a->_left)) ? 0 : 1; // _e: horizontal range - _f = ((_di >= a->_top + a->height()) || (_di <= a->_top)) ? 0 : 1; // _f: vertical range + _e = ((_si >= a->getFrameX() + a->width()) || (_si <= a->getFrameX())) ? 0 : 1; // _e: horizontal range + _f = ((_di >= a->getFrameY() + a->height()) || (_di <= a->getFrameY())) ? 0 : 1; // _f: vertical range _b = ((type != 0) || (a->_type == kZoneYou)) ? 0 : 1; // _b: (no type specified) AND (Animation is not the character) _c = (a->_type & 0xFFFF0000) ? 0 : 1; // _c: Animation is not an object diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index c19d6ae5e5..1c2cb58b5b 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -377,6 +377,12 @@ void Gfx::beginFrame() { *data++ = _backgroundInfo->mask.getValue(x, y); } } +#if 1 + Common::DumpFile dump; + dump.open("maskdump.bin"); + dump.write(_bitmapMask.pixels, _bitmapMask.w * _bitmapMask.h); + dump.close(); +#endif break; } } @@ -441,7 +447,7 @@ void Gfx::updateScreen() { for (; b != e; b++) { ZonePtr z = *b; if (z->_type & kZonePath) { - surf->frameRect(Common::Rect(z->_left, z->_top, z->_right, z->_bottom), 2); + surf->frameRect(Common::Rect(z->getX(), z->getY(), z->getX() + z->width(), z->getY() + z->height()), 2); } } g_system->unlockScreen(); diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index 23b4569c6a..471f71dfa8 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -209,6 +209,42 @@ public: return (m >> n) & 3; } + inline byte* getPtr(uint16 x, uint16 y) const { + return data + (x >> 2) + y * internalWidth; + } + + void bltOr(uint16 dx, uint16 dy, const MaskBuffer &src, uint16 sx, uint16 sy, uint width, uint height) { + assert((width <= w) && (width <= src.w) && (height <= h) && (height <= src.h)); + + byte *s = src.getPtr(sx, sy); + byte *d = getPtr(dx, dy); + + // this code assumes buffers are aligned on 4-pixels boundaries, as the original does + uint16 linewidth = width >> 2; + for (uint16 i = 0; i < height; i++) { + for (uint16 j = 0; j < linewidth; j++) { + *d++ |= *s++; + } + d += internalWidth - linewidth; + s += src.internalWidth - linewidth; + } + } + + void bltCopy(uint16 dx, uint16 dy, const MaskBuffer &src, uint16 sx, uint16 sy, uint width, uint height) { + assert((width <= w) && (width <= src.w) && (height <= h) && (height <= src.h)); + + byte *s = src.getPtr(sx, sy); + byte *d = getPtr(dx, dy); + + // this code assumes buffers are aligned on 4-pixels boundaries, as the original does + for (uint16 i = 0; i < height; i++) { + memcpy(d, s, (width >> 2)); + d += internalWidth; + s += src.internalWidth; + } + } + + }; @@ -419,7 +455,9 @@ struct BackgroundInfo { int layers[4]; PaletteFxRange ranges[6]; - BackgroundInfo() : x(0), y(0), width(0), height(0) { + bool hasMask; + + BackgroundInfo() : x(0), y(0), width(0), height(0), hasMask(false) { layers[0] = layers[1] = layers[2] = layers[3] = 0; memset(ranges, 0, sizeof(ranges)); } diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp index c387484de7..d2332643ed 100644 --- a/engines/parallaction/objects.cpp +++ b/engines/parallaction/objects.cpp @@ -71,6 +71,20 @@ uint16 Animation::height() const { return r.height(); } +int16 Animation::getFrameX() const { + if (!gfxobj) return _left; + Common::Rect r; + gfxobj->getRect(_frame, r); + return r.left + _left; +} + +int16 Animation::getFrameY() const { + if (!gfxobj) return _top; + Common::Rect r; + gfxobj->getRect(_frame, r); + return r.top + _top; +} + uint16 Animation::getFrameNum() const { if (!gfxobj) return 0; return gfxobj->getNum(); @@ -115,24 +129,29 @@ int16 Program::addLocal(const char *name, int16 value, int16 min, int16 max) { assert(_numLocals < NUM_LOCALS); strcpy(_localNames[_numLocals], name); - _locals[_numLocals]._value = value; - - _locals[_numLocals]._min = min; - _locals[_numLocals]._max = max; + _locals[_numLocals].setRange(min, max); + _locals[_numLocals].setValue(value); return _numLocals++; } -void LocalVariable::wrap() { +void LocalVariable::setValue(int16 value) { + if (value >= _max) + value = _min; + if (value < _min) + value = _max - 1; - if (_value >= _max) - _value = _min; - if (_value < _min) - _value = _max - 1; + _value = value; +} - return; +void LocalVariable::setRange(int16 min, int16 max) { + _max = max; + _min = min; } +int16 LocalVariable::getValue() const { + return _value; +} Zone::Zone() { @@ -193,13 +212,6 @@ Zone::~Zone() { free(_linkedName); } -void Zone::getRect(Common::Rect& r) const { - r.left = _left; - r.right = _right; - r.top = _top; - r.bottom = _bottom; -} - void Zone::translate(int16 x, int16 y) { _left += x; _right += x; @@ -273,18 +285,18 @@ Instruction::~Instruction() { free(_text2); } -int16 ScriptVar::getRValue() { +int16 ScriptVar::getValue() { if (_flags & kParaImmediate) { return _value; } if (_flags & kParaLocal) { - return _local->_value; + return _local->getValue(); } if (_flags & kParaField) { - return *_pvalue; + return _field->getValue(); } if (_flags & kParaRandom) { @@ -296,27 +308,33 @@ int16 ScriptVar::getRValue() { return 0; } -int16* ScriptVar::getLValue() { +void ScriptVar::setValue(int16 value) { + if ((_flags & kParaLValue) == 0) { + error("Only l-value can be set"); + } if (_flags & kParaLocal) { - return &_local->_value; + _local->setValue(value); } if (_flags & kParaField) { - return _pvalue; + _field->setValue(value); } - error("Parameter is not an l-value"); - } void ScriptVar::setLocal(LocalVariable *local) { _local = local; - _flags |= kParaLocal; + _flags |= (kParaLocal | kParaLValue); } -void ScriptVar::setField(int16 *field) { - _pvalue = field; +void ScriptVar::setField(Animation *anim, AnimationField::AccessorFunc accessor, AnimationField::MutatorFunc mutator) { + _field = new AnimationField(anim, accessor, mutator); + _flags |= (kParaField | kParaLValue); +} + +void ScriptVar::setField(Animation *anim, AnimationField::AccessorFunc accessor) { + _field = new AnimationField(anim, accessor); _flags |= kParaField; } @@ -335,9 +353,14 @@ ScriptVar::ScriptVar() { _flags = 0; _local = 0; _value = 0; - _pvalue = 0; + _field = 0; } +ScriptVar::~ScriptVar() { + delete _field; +} + + Table::Table(uint32 size) : _size(size), _used(0), _disposeMemory(true) { _data = (char**)calloc(size, sizeof(char*)); } diff --git a/engines/parallaction/objects.h b/engines/parallaction/objects.h index a3bf757bdb..15550c65c6 100644 --- a/engines/parallaction/objects.h +++ b/engines/parallaction/objects.h @@ -198,11 +198,14 @@ struct GetData { // size = 24 byte *_backup; uint16 field_14; // unused uint16 field_16; // unused + MaskBuffer _mask[2]; + bool hasMask; GetData() { _icon = 0; _backup = NULL; gfxobj = NULL; + hasMask = false; } }; struct SpeakData { // size = 36 @@ -297,17 +300,19 @@ struct TypeData { #define ZONENAME_LENGTH 32 struct Zone { - char _name[ZONENAME_LENGTH]; - +protected: int16 _left; int16 _top; int16 _right; int16 _bottom; + +public: + char _name[ZONENAME_LENGTH]; + uint32 _type; uint32 _flags; uint _label; - uint16 field_2C; // unused - uint16 field_2E; // unused + TypeData u; CommandList _commands; Common::Point _moveTo; @@ -320,32 +325,101 @@ struct Zone { Zone(); virtual ~Zone(); - void getRect(Common::Rect& r) const; void translate(int16 x, int16 y); virtual uint16 width() const; virtual uint16 height() const; + + void setBox(int16 left, int16 top, int16 right, int16 bottom) { + setX(left); + setY(top); + _right = right; + _bottom = bottom; + } + + void getBox(Common::Rect& r) { + r.left = getX(); + r.right = getX() + width(); + r.top = getY(); + r.bottom = getY() + height(); + } + + + // getters/setters + virtual int16 getX() { return _left; } + virtual void setX(int16 value) { _left = value; } + + virtual int16 getY() { return _top; } + virtual void setY(int16 value) { _top = value; } }; struct LocalVariable { +protected: int16 _value; int16 _min; int16 _max; +public: + LocalVariable() { _value = 0; _min = -10000; _max = 10000; } - void wrap(); + void setRange(int16 min, int16 max); + + int16 getValue() const; + void setValue(int16 value); }; + enum ParaFlags { kParaImmediate = 1, // instruction is using an immediate parameter kParaLocal = 2, // instruction is using a local variable kParaField = 0x10, // instruction is using an animation's field - kParaRandom = 0x100 + kParaRandom = 0x100, + + kParaLValue = 0x20 +}; + + +struct AnimationField { + typedef Common::Functor0Mem<int16, Animation> Accessor; + typedef Common::Functor1Mem<int16, void, Animation> Mutator; + + typedef Accessor::FuncType AccessorFunc; + typedef Mutator::FuncType MutatorFunc; + +protected: + Accessor *_accessor; + Mutator *_mutator; + +public: + AnimationField(Animation* instance, AccessorFunc accessor, MutatorFunc mutator) { + _accessor = new Accessor(instance, accessor); + _mutator = new Mutator(instance, mutator); + } + + AnimationField(Animation* instance, AccessorFunc accessor) { + _accessor = new Accessor(instance, accessor); + _mutator = 0; + } + + ~AnimationField() { + delete _accessor; + delete _mutator; + } + + int16 getValue() const { + assert(_accessor); + return _accessor->operator()(); + } + + void setValue(int16 value) { + assert(_mutator); + _mutator->operator()(value); + } }; @@ -353,16 +427,18 @@ struct ScriptVar { uint32 _flags; int16 _value; - int16* _pvalue; LocalVariable* _local; + AnimationField* _field; ScriptVar(); + ~ScriptVar(); - int16 getRValue(); - int16* getLValue(); + int16 getValue(); + void setValue(int16 value); void setLocal(LocalVariable *local); - void setField(int16 *field); + void setField(Animation *anim, AnimationField::AccessorFunc accessor, AnimationField::MutatorFunc mutator); + void setField(Animation *anim, AnimationField::AccessorFunc accessor); void setImmediate(int16 value); void setRandom(int16 seed); }; @@ -430,19 +506,13 @@ typedef Common::SharedPtr<Program> ProgramPtr; typedef Common::List<ProgramPtr> ProgramList; struct Animation : public Zone { +protected: + int16 _frame; + int16 _z; +public: - Common::Point _oldPos; GfxObj *gfxobj; char *_scriptName; - int16 _frame; - uint16 field_50; // unused - int16 _z; - uint16 field_54; // unused - uint16 field_56; // unused - uint16 field_58; // unused - uint16 field_5A; // unused - uint16 field_5C; // unused - uint16 field_5E; // unused Animation(); virtual ~Animation(); @@ -452,6 +522,22 @@ struct Animation : public Zone { byte* getFrameData(uint32 index) const; void validateScriptVars(); + + int16 getFrameX() const; + int16 getFrameY() const; + + // getters/setters used by scripts + int16 getX() { return _left; } + void setX(int16 value) { _left = value; } + + int16 getY() { return _top; } + void setY(int16 value) { _top = value; } + + int16 getZ() { return _z; } + void setZ(int16 value) { _z = value; } + + int16 getF() { return _frame; } + void setF(int16 value) { _frame = value; } }; class Table { diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index bb306c3299..c810d22b33 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -53,7 +53,7 @@ uint32 _engineFlags = 0; uint16 _score = 1; char _password[8]; -uint32 _commandFlags = 0; +uint32 _globalFlags = 0; // private stuff @@ -85,7 +85,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam Parallaction::~Parallaction() { delete _debugger; - delete _globalTable; + delete _globalFlagsNames; delete _callableNames; delete _cmdExec; delete _programExec; @@ -114,7 +114,7 @@ int Parallaction::init() { _engineFlags = 0; _objectsNames = NULL; - _globalTable = NULL; + _globalFlagsNames = NULL; _location._hasSound = false; _baseTime = 0; _numLocations = 0; @@ -352,9 +352,9 @@ void Parallaction::runGame() { if (_input->_inputMode == Input::kInputModeGame) { _programExec->runScripts(_location._programs.begin(), _location._programs.end()); - _char._ani->_z = _char._ani->height() + _char._ani->_top; + _char._ani->setZ(_char._ani->height() + _char._ani->getFrameY()); if (_char._ani->gfxobj) { - _char._ani->gfxobj->z = _char._ani->_z; + _char._ani->gfxobj->z = _char._ani->getZ(); } _char._walker->walk(); drawAnimations(); @@ -452,7 +452,7 @@ void Parallaction::freeZones() { // NOTE : this condition has been relaxed compared to the original, to allow the engine // to retain special - needed - zones that were lost across location switches. ZonePtr z = *it; - if (((z->_top == -1) || (z->_left == -2)) && ((_engineFlags & kEngineQuit) == 0)) { + if (((z->getY() == -1) || (z->getX() == -2)) && ((_engineFlags & kEngineQuit) == 0)) { debugC(2, kDebugExec, "freeZones preserving zone '%s'", z->_name); it++; } else { @@ -510,12 +510,10 @@ Character::Character(Parallaction *vm) : _vm(vm), _ani(new Animation) { _dummy = false; - _ani->_left = 150; - _ani->_top = 100; - _ani->_z = 10; - _ani->_oldPos.x = -1000; - _ani->_oldPos.y = -1000; - _ani->_frame = 0; + _ani->setX(150); + _ani->setY(100); + _ani->setZ(10); + _ani->setF(0); _ani->_flags = kFlagsActive | kFlagsNoName; _ani->_type = kZoneYou; strncpy(_ani->_name, "yourself", ZONENAME_LENGTH); @@ -542,18 +540,18 @@ Character::~Character() { void Character::getFoot(Common::Point &foot) { Common::Rect rect; - _ani->gfxobj->getRect(_ani->_frame, rect); + _ani->gfxobj->getRect(_ani->getF(), rect); - foot.x = _ani->_left + (rect.left + rect.width() / 2); - foot.y = _ani->_top + (rect.top + rect.height()); + foot.x = _ani->getX() + (rect.left + rect.width() / 2); + foot.y = _ani->getY() + (rect.top + rect.height()); } void Character::setFoot(const Common::Point &foot) { Common::Rect rect; - _ani->gfxobj->getRect(_ani->_frame, rect); + _ani->gfxobj->getRect(_ani->getF(), rect); - _ani->_left = foot.x - (rect.left + rect.width() / 2); - _ani->_top = foot.y - (rect.top + rect.height()); + _ani->setX(foot.x - (rect.left + rect.width() / 2)); + _ani->setY(foot.y - (rect.top + rect.height())); } #if 0 @@ -677,7 +675,7 @@ void Character::updateDirection(const Common::Point& pos, const Common::Point& t _step++; if (dist.x == 0 && dist.y == 0) { - _ani->_frame = frames->stillFrame[_direction]; + _ani->setF(frames->stillFrame[_direction]); return; } @@ -687,7 +685,7 @@ void Character::updateDirection(const Common::Point& pos, const Common::Point& t dist.y = -dist.y; _direction = (dist.x > dist.y) ? ((to.x > pos.x) ? WALK_LEFT : WALK_RIGHT) : ((to.y > pos.y) ? WALK_DOWN : WALK_UP); - _ani->_frame = frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]; + _ani->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]); } diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index e5c5221414..fb004a25b7 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -136,7 +136,7 @@ extern uint16 _score; extern uint16 _language; extern uint32 _engineFlags; extern char _saveData1[]; -extern uint32 _commandFlags; +extern uint32 _globalFlags; extern const char *_dinoName; extern const char *_donnaName; extern const char *_doughName; @@ -263,7 +263,7 @@ public: ZonePtr findZone(const char *name); ZonePtr hitZone(uint32 type, uint16 x, uint16 y); - uint16 runZone(ZonePtr z); + void runZone(ZonePtr z); void freeZones(); AnimationPtr findAnimation(const char *name); @@ -272,7 +272,7 @@ public: void setBackground(const char *background, const char *mask, const char *path); void freeBackground(); - Table *_globalTable; + Table *_globalFlagsNames; Table *_objectsNames; Table *_callableNames; Table *_localFlagNames; @@ -354,7 +354,7 @@ protected: // members void freeCharacter(); - int16 pickupItem(ZonePtr z); + bool pickupItem(ZonePtr z); void clearSet(OpcodeSet &opcodes); @@ -370,7 +370,7 @@ public: virtual void parseLocation(const char* name) = 0; - void updateDoor(ZonePtr z); + void updateDoor(ZonePtr z, bool close); virtual void drawAnimations() = 0; @@ -424,6 +424,8 @@ public: void setInternLanguage(uint id); uint getInternLanguage(); + + void showZone(ZonePtr z, bool visible); }; @@ -623,8 +625,10 @@ public: int _part; int _progress; +#if 0 // disabled since I couldn't find any references to lip sync in the scripts int16 _lipSyncVal; uint _subtitleLipSync; +#endif int _subtitleY; int _subtitle[2]; diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 761c8d1b74..bb8ddc5654 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -193,7 +193,7 @@ void Parallaction_br::initPart() { memset(_counters, 0, ARRAYSIZE(_counters)); - _globalTable = _disk->loadTable("global"); + _globalFlagsNames = _disk->loadTable("global"); _objectsNames = _disk->loadTable("objects"); _countersNames = _disk->loadTable("counters"); @@ -208,11 +208,11 @@ void Parallaction_br::initPart() { void Parallaction_br::freePart() { - delete _globalTable; + delete _globalFlagsNames; delete _objectsNames; delete _countersNames; - _globalTable = 0; + _globalFlagsNames = 0; _objectsNames = 0; _countersNames = 0; } diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp index e81492e655..61f2859e8a 100644 --- a/engines/parallaction/parallaction_ns.cpp +++ b/engines/parallaction/parallaction_ns.cpp @@ -218,7 +218,7 @@ void Parallaction_ns::callFunction(uint index, void* parm) { int Parallaction_ns::go() { renameOldSavefiles(); - _globalTable = _disk->loadTable("global"); + _globalFlagsNames = _disk->loadTable("global"); startGui(); @@ -318,14 +318,10 @@ void Parallaction_ns::changeLocation(char *location) { strcpy(_saveData1, locname.location()); parseLocation(_saveData1); - _char._ani->_oldPos.x = -1000; - _char._ani->_oldPos.y = -1000; - - _char._ani->field_50 = 0; if (_location._startPosition.x != -1000) { - _char._ani->_left = _location._startPosition.x; - _char._ani->_top = _location._startPosition.y; - _char._ani->_frame = _location._startFrame; + _char._ani->setX(_location._startPosition.x); + _char._ani->setY(_location._startPosition.y); + _char._ani->setF(_location._startFrame); _location._startPosition.y = -1000; _location._startPosition.x = -1000; } @@ -438,7 +434,7 @@ void Parallaction_ns::cleanupGame() { // this code saves main character animation from being removed from the following code _location._animations.remove(_char._ani); _numLocations = 0; - _commandFlags = 0; + _globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); diff --git a/engines/parallaction/parser.cpp b/engines/parallaction/parser.cpp index 6de0a7d7f5..8e30a631e4 100644 --- a/engines/parallaction/parser.cpp +++ b/engines/parallaction/parser.cpp @@ -28,7 +28,10 @@ namespace Parallaction { -char _tokens[20][MAX_TOKEN_LEN]; +#define MAX_TOKENS 50 + +int _numTokens; +char _tokens[MAX_TOKENS][MAX_TOKEN_LEN]; Script::Script(Common::ReadStream *input, bool disposeSource) : _input(input), _disposeSource(disposeSource), _line(0) {} @@ -65,9 +68,11 @@ char *Script::readLine(char *buf, size_t bufSize) { void Script::clearTokens() { - for (uint16 i = 0; i < 20; i++) + for (uint16 i = 0; i < MAX_TOKENS; i++) _tokens[i][0] = '\0'; + _numTokens = 0; + return; } @@ -88,7 +93,7 @@ void Script::skip(const char* endToken) { // // The routine returns the unparsed portion of the input string 's'. // -char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes) { +char *Script::parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes) { enum STATES { NORMAL, QUOTED }; @@ -151,12 +156,14 @@ char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ign uint16 Script::fillTokens(char* line) { uint16 i = 0; - while (strlen(line) > 0 && i < 20) { + while (strlen(line) > 0 && i < MAX_TOKENS) { line = parseNextToken(line, _tokens[i], MAX_TOKEN_LEN, " \t\n"); line = Common::ltrim(line); i++; } + _numTokens = i; + return i; } @@ -243,4 +250,185 @@ void Parser::parseStatement() { } +#define BLOCK_BASE 100 + +class StatementDef { +protected: + Common::String makeLineFromTokens() { + Common::String space(" "); + Common::String newLine("\n"); + Common::String text; + for (int i = 0; i < _numTokens; i++) + text += (Common::String(_tokens[i]) + space); + text.deleteLastChar(); + text += newLine; + return text; + } + +public: + uint _score; + const char* _name; + + + StatementDef(uint score, const char *name) : _score(score), _name(name) { } + virtual ~StatementDef() { } + + virtual Common::String makeLine(Script &script) = 0; + +}; + + +class SimpleStatementDef : public StatementDef { + +public: + SimpleStatementDef(uint score, const char *name) : StatementDef(score, name) { } + + Common::String makeLine(Script &script) { + return makeLineFromTokens(); + } + +}; + + + +class BlockStatementDef : public StatementDef { + + const char* _ending1; + const char* _ending2; + +public: + BlockStatementDef(uint score, const char *name, const char *ending1, const char *ending2 = 0) : StatementDef(score, name), _ending1(ending1), + _ending2(ending2) { } + + Common::String makeLine(Script &script) { + Common::String text = makeLineFromTokens(); + bool end; + do { + script.readLineToken(true); + text += makeLineFromTokens(); + end = !scumm_stricmp(_ending1, _tokens[0]) || (_ending2 && !scumm_stricmp(_ending2, _tokens[0])); + } while (!end); + return text; + } + +}; + +class CommentStatementDef : public StatementDef { + + Common::String parseComment(Script &script) { + Common::String result; + char buf[401]; + + do { + script.readLine(buf, 400); + buf[strlen(buf)-1] = '\0'; + if (!scumm_stricmp(buf, "endtext")) + break; + result += Common::String(buf) + "\n"; + } while (true); + result += "endtext\n"; + return result; + } + +public: + CommentStatementDef(uint score, const char *name) : StatementDef(score, name) { } + + Common::String makeLine(Script &script) { + Common::String text = makeLineFromTokens(); + text += parseComment(script); + return text; + } + +}; + + + + +PreProcessor::PreProcessor() { + _defs.push_back(new SimpleStatementDef(1, "disk" )); + _defs.push_back(new SimpleStatementDef(2, "location" )); + _defs.push_back(new SimpleStatementDef(3, "localflags" )); + _defs.push_back(new SimpleStatementDef(4, "flags" )); + _defs.push_back(new SimpleStatementDef(5, "zeta" )); + _defs.push_back(new SimpleStatementDef(6, "music" )); + _defs.push_back(new SimpleStatementDef(7, "sound" )); + _defs.push_back(new SimpleStatementDef(8, "mask" )); + _defs.push_back(new SimpleStatementDef(9, "path" )); + _defs.push_back(new SimpleStatementDef(10, "character" )); + _defs.push_back(new CommentStatementDef(11, "comment" )); + _defs.push_back(new CommentStatementDef(12, "endcomment" )); + _defs.push_back(new BlockStatementDef(13, "ifchar", "endif" )); + _defs.push_back(new BlockStatementDef(BLOCK_BASE, "zone", "endanimation", "endzone" )); + _defs.push_back(new BlockStatementDef(BLOCK_BASE, "animation", "endanimation", "endzone" )); + _defs.push_back(new BlockStatementDef(1000, "commands", "endcommands" )); + _defs.push_back(new BlockStatementDef(1001, "acommands", "endcommands" )); + _defs.push_back(new BlockStatementDef(1002, "escape", "endcommands" )); + _defs.push_back(new SimpleStatementDef(2000, "endlocation")); +} + +PreProcessor::~PreProcessor() { + DefList::iterator it = _defs.begin(); + for (; it != _defs.end(); it++) { + delete *it; + } +} + +StatementDef* PreProcessor::findDef(const char* name) { + DefList::iterator it = _defs.begin(); + for (; it != _defs.end(); it++) { + if (!scumm_stricmp((*it)->_name, name)) { + return *it; + } + } + return 0; +} + + + +uint PreProcessor::getDefScore(StatementDef* def) { + if (def->_score == BLOCK_BASE) { + _numZones++; + return (_numZones + BLOCK_BASE); + } + return def->_score; +} + + +void PreProcessor::preprocessScript(Script &script, StatementList &list) { + _numZones = 0; + Common::String text; + do { + script.readLineToken(false); + if (_numTokens == 0) + break; + + StatementDef *def = findDef(_tokens[0]); + assert(def); + + text = def->makeLine(script); + int score = getDefScore(def); + list.push_back(StatementListNode(score, def->_name, text)); + } while (true); + Common::sort(list.begin(), list.end()); +} + + + + +void testPreprocessing(Parallaction *vm, const char *filename) { + Script *script = vm->_disk->loadLocation(filename); + StatementList list; + PreProcessor pp; + pp.preprocessScript(*script, list); + delete script; + Common::DumpFile dump; + dump.open(filename); + StatementList::iterator it = list.begin(); + for ( ; it != list.end(); it++) { + dump.write((*it)._text.c_str(), (*it)._text.size()); + } + dump.close(); +} + + } // namespace Parallaction diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h index 79e6cf6640..e622bfd81f 100644 --- a/engines/parallaction/parser.h +++ b/engines/parallaction/parser.h @@ -32,9 +32,8 @@ namespace Parallaction { -char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes = false); - #define MAX_TOKEN_LEN 50 +extern int _numTokens; extern char _tokens[][MAX_TOKEN_LEN]; class Script { @@ -45,6 +44,7 @@ class Script { void clearTokens(); uint16 fillTokens(char* line); + char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes = false); public: Script(Common::ReadStream *, bool _disposeSource = false); @@ -63,6 +63,7 @@ typedef Common::Functor0<void> Opcode; typedef Common::Array<const Opcode*> OpcodeSet; + class Parser { public: @@ -95,6 +96,7 @@ public: class Parallaction_ns; class Parallaction_br; + class LocationParser_ns { protected: @@ -129,9 +131,6 @@ protected: // BRA specific int numZones; BackgroundInfo *info; - char *bgName; - char *maskName; - char *pathName; char *characterName; } ctxt; @@ -240,6 +239,23 @@ public: }; +/* + TODO: adapt the parser to effectively use the + statement list provided by preprocessor as its + input, instead of relying on the current Script + class. + + This would need a major rewrite of the parsing + system! + + parseNextToken could then be sealed into the + PreProcessor class forever, together with the + _tokens[] and _numTokens stuff, now dangling as + global objects. + + NS balloons code should be dealt with before, + though. +*/ class LocationParser_br : public LocationParser_ns { protected: @@ -287,6 +303,7 @@ protected: virtual void parseZoneTypeBlock(ZonePtr z); void parsePathData(ZonePtr z); + void parseGetData(ZonePtr z); public: LocationParser_br(Parallaction_br *vm) : LocationParser_ns((Parallaction_ns*)vm), _vm(vm) { @@ -402,6 +419,117 @@ public: }; + +/* + This simple stream is temporarily needed to hook the + preprocessor output to the parser. It will go away + when the parser is rewritten to fully exploit the + statement list provided by the preprocessor. +*/ + +class ReadStringStream : public Common::ReadStream { + + char *_text; + uint32 _pos; + uint32 _size; + +public: + ReadStringStream(const Common::String &text) { + _text = new char[text.size() + 1]; + strcpy(_text, text.c_str()); + _size = text.size(); + _pos = 0; + } + + ~ReadStringStream() { + delete []_text; + } + + uint32 read(void *buffer, uint32 size) { + if (_pos + size > _size) { + size = _size - _pos; + } + memcpy(buffer, _text + _pos, size); + _pos += size; + return size; + } + + bool eos() const { + return _pos == _size; + } + +}; + + +/* + Demented as it may sound, the success of a parsing operation in the + original BRA depends on what has been parsed before. The game features + an innovative chaos system that involves the parser and the very game + engine, in order to inflict the user an unforgettable game experience. + + Ok, now for the serious stuff. + + The PreProcessor implemented here fixes the location scripts before + they are fed to the parser. It tries to do so by a preliminary scan + of the text file, during which a score is assigned to each statement + (more on this later). When the whole file has been analyzed, the + statements are sorted according to their score, to create a parsable + sequence. + + For parsing, the statements in location scripts can be conveniently + divided into 3 groups: + + * location definitions + * element definitions + * start-up commands + + Since the parsing of element definitions requires location parameters + to be set, location definitions should be encountered first in the + script. Start-up commands in turn may reference elements, so they can + be parsed last. The first goal is to make sure the parser gets these + three sets in this order. + + Location definitions must also be presented in a certain sequence, + because resource files are not fully self-describing. In short, some + critical game data in contained in certain files, that must obviously + be read before any other can be analyzed. This is the second goal. + + TODO: some words about actual implementation. +*/ + +class StatementDef; + +struct StatementListNode { + int _score; + Common::String _name; + Common::String _text; + + StatementListNode(int score, const Common::String &name, const Common::String &text) : _score(score), _name(name), _text(text) { } + + bool operator<(const StatementListNode& node) const { + return _score < node._score; + } +}; +typedef Common::List<StatementListNode> StatementList; + + +class PreProcessor { + typedef Common::List<StatementDef*> DefList; + + int _numZones; + DefList _defs; + + StatementDef* findDef(const char* name); + uint getDefScore(StatementDef*); + +public: + PreProcessor(); + ~PreProcessor(); + void preprocessScript(Script &script, StatementList &list); +}; + + + } // namespace Parallaction #endif diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp index 3b446805d7..20800abc33 100644 --- a/engines/parallaction/parser_br.cpp +++ b/engines/parallaction/parser_br.cpp @@ -25,6 +25,7 @@ #include "parallaction/parallaction.h" + #include "parallaction/sound.h" namespace Parallaction { @@ -104,6 +105,7 @@ namespace Parallaction { #define INST_ENDIF 30 #define INST_STOP 31 + const char *_zoneTypeNamesRes_br[] = { "examine", "door", @@ -314,7 +316,6 @@ DECLARE_LOCATION_PARSER(location) { debugC(7, kDebugParser, "LOCATION_PARSER(location) "); strcpy(_vm->_location._name, _tokens[1]); - ctxt.bgName = strdup(_tokens[1]); bool flip = false; int nextToken; @@ -329,15 +330,17 @@ DECLARE_LOCATION_PARSER(location) { // TODO: handle background horizontal flip (via a context parameter) if (_tokens[nextToken][0] != '\0') { - _vm->_char._ani->_left = atoi(_tokens[nextToken]); + _vm->_char._ani->setX(atoi(_tokens[nextToken])); nextToken++; - _vm->_char._ani->_top = atoi(_tokens[nextToken]); + _vm->_char._ani->setY(atoi(_tokens[nextToken])); nextToken++; } if (_tokens[nextToken][0] != '\0') { - _vm->_char._ani->_frame = atoi(_tokens[nextToken]); + _vm->_char._ani->setF(atoi(_tokens[nextToken])); } + + _vm->_disk->loadScenery(*ctxt.info, _tokens[1], 0, 0); } @@ -463,17 +466,20 @@ DECLARE_LOCATION_PARSER(null) { DECLARE_LOCATION_PARSER(mask) { debugC(7, kDebugParser, "LOCATION_PARSER(mask) "); - ctxt.maskName = strdup(_tokens[1]); - ctxt.info->layers[0] = atoi(_tokens[2]); - ctxt.info->layers[1] = atoi(_tokens[3]); - ctxt.info->layers[2] = atoi(_tokens[4]); + ctxt.info->layers[0] = 0; + ctxt.info->layers[1] = atoi(_tokens[2]); + ctxt.info->layers[2] = atoi(_tokens[3]); + ctxt.info->layers[3] = atoi(_tokens[4]); + + _vm->_disk->loadScenery(*ctxt.info, 0, _tokens[1], 0); + ctxt.info->hasMask = true; } DECLARE_LOCATION_PARSER(path) { debugC(7, kDebugParser, "LOCATION_PARSER(path) "); - ctxt.pathName = strdup(_tokens[1]); + _vm->_disk->loadScenery(*ctxt.info, 0, 0, _tokens[1]); } @@ -714,10 +720,7 @@ DECLARE_ZONE_PARSER(limits) { ctxt.z->_linkedAnim = _vm->findAnimation(_tokens[1]); ctxt.z->_linkedName = strdup(_tokens[1]); } else { - ctxt.z->_left = atoi(_tokens[1]); - ctxt.z->_top = atoi(_tokens[2]); - ctxt.z->_right = atoi(_tokens[3]); - ctxt.z->_bottom = atoi(_tokens[4]); + ctxt.z->setBox(atoi(_tokens[1]), atoi(_tokens[2]), atoi(_tokens[3]), atoi(_tokens[4])); } } @@ -768,6 +771,49 @@ void LocationParser_br::parsePathData(ZonePtr z) { z->u.path = data; } +void LocationParser_br::parseGetData(ZonePtr z) { + + GetData *data = new GetData; + + do { + + if (!scumm_stricmp(_tokens[0], "file")) { + + GfxObj *obj = _vm->_gfx->loadGet(_tokens[1]); + obj->frame = 0; + obj->x = z->getX(); + obj->y = z->getY(); + data->gfxobj = obj; + } + + if (!scumm_stricmp(_tokens[0], "mask")) { + if (ctxt.info->hasMask) { + Common::Rect rect; + data->gfxobj->getRect(0, rect); + data->_mask[0].create(rect.width(), rect.height()); + _vm->_disk->loadMask(_tokens[1], data->_mask[0]); + data->_mask[1].create(rect.width(), rect.height()); + data->_mask[1].bltCopy(0, 0, ctxt.info->mask, data->gfxobj->x, data->gfxobj->y, data->_mask->w, data->_mask->h); + data->hasMask = true; + } else { + warning("Mask for zone '%s' ignored, since background doesn't have one", z->_name); + } + } + + if (!scumm_stricmp(_tokens[0], "path")) { + + } + + if (!scumm_stricmp(_tokens[0], "icon")) { + data->_icon = 4 + _vm->_objectsNames->lookup(_tokens[1]); + } + + _script->readLineToken(true); + } while (scumm_stricmp(_tokens[0], "endzone")); + + z->u.get = data; +} + void LocationParser_br::parseZoneTypeBlock(ZonePtr z) { debugC(7, kDebugParser, "parseZoneTypeBlock(name: %s, type: %x)", z->_name, z->_type); @@ -822,10 +868,10 @@ DECLARE_ANIM_PARSER(file) { DECLARE_ANIM_PARSER(position) { debugC(7, kDebugParser, "ANIM_PARSER(position) "); - ctxt.a->_left = atoi(_tokens[1]); - ctxt.a->_top = atoi(_tokens[2]); - ctxt.a->_z = atoi(_tokens[3]); - ctxt.a->_frame = atoi(_tokens[4]); + ctxt.a->setX(atoi(_tokens[1])); + ctxt.a->setY(atoi(_tokens[2])); + ctxt.a->setZ(atoi(_tokens[3])); + ctxt.a->setF(atoi(_tokens[4])); } @@ -841,15 +887,14 @@ DECLARE_ANIM_PARSER(moveto) { DECLARE_ANIM_PARSER(endanimation) { debugC(7, kDebugParser, "ANIM_PARSER(endanimation) "); - +#if 0 + // I have disabled the following code since it seems useless. + // I will remove it after mask processing is done. if (ctxt.a->gfxobj) { ctxt.a->_right = ctxt.a->width(); ctxt.a->_bottom = ctxt.a->height(); } - - ctxt.a->_oldPos.x = -1000; - ctxt.a->_oldPos.y = -1000; - +#endif ctxt.a->_flags |= 0x1000000; _parser->popTables(); @@ -992,16 +1037,16 @@ void ProgramParser_br::parseRValue(ScriptVar &v, const char *str) { a = AnimationPtr(ctxt.a); if (str[0] == 'X') { - v.setField(&a->_left); + v.setField(a.get(), &Animation::getX); } else if (str[0] == 'Y') { - v.setField(&a->_top); + v.setField(a.get(), &Animation::getY); } else if (str[0] == 'Z') { - v.setField(&a->_z); + v.setField(a.get(), &Animation::getZ); } else if (str[0] == 'F') { - v.setField(&a->_frame); + v.setField(a.get(), &Animation::getF); } else if (str[0] == 'N') { v.setImmediate(a->getFrameNum()); @@ -1010,7 +1055,10 @@ void ProgramParser_br::parseRValue(ScriptVar &v, const char *str) { v.setRandom(atoi(&str[1])); } else if (str[0] == 'L') { +#if 0 // disabled because no references to lip sync has been found in the scripts v.setField(&_vm->_lipSyncVal); +#endif + warning("Lip sync instruction encountered! Please notify the team!"); } } @@ -1168,31 +1216,50 @@ void ProgramParser_br::init() { INSTRUCTION_PARSER(endscript); } + +/* + Ancillary routine to support hooking preprocessor and + parser. +*/ +Common::ReadStream *getStream(StatementList &list) { + Common::String text; + StatementList::iterator it = list.begin(); + for ( ; it != list.end(); it++) { + text += (*it)._text; + } + return new ReadStringStream(text); +} + void LocationParser_br::parse(Script *script) { + PreProcessor pp; + StatementList list; + pp.preprocessScript(*script, list); + Script *script2 = new Script(getStream(list), true); + ctxt.numZones = 0; - ctxt.bgName = 0; - ctxt.maskName = 0; - ctxt.pathName = 0; ctxt.characterName = 0; ctxt.info = new BackgroundInfo; - LocationParser_ns::parse(script); + LocationParser_ns::parse(script2); - _vm->_disk->loadScenery(*ctxt.info, ctxt.bgName, ctxt.maskName, ctxt.pathName); _vm->_gfx->setBackground(kBackgroundLocation, ctxt.info); _vm->_pathBuffer = &ctxt.info->path; + ZoneList::iterator it = _vm->_location._zones.begin(); + for ( ; it != _vm->_location._zones.end(); it++) { + bool visible = ((*it)->_flags & kFlagsRemove) == 0; + _vm->showZone((*it), visible); + } + if (ctxt.characterName) { _vm->changeCharacter(ctxt.characterName); } - free(ctxt.bgName); - free(ctxt.maskName); - free(ctxt.pathName); free(ctxt.characterName); + delete script2; } } // namespace Parallaction diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index ad0f714fdc..4b90e2364f 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -221,9 +221,6 @@ DECLARE_ANIM_PARSER(type) { } } - ctxt.a->_oldPos.x = -1000; - ctxt.a->_oldPos.y = -1000; - ctxt.a->_flags |= 0x1000000; _parser->popTables(); @@ -267,9 +264,9 @@ DECLARE_ANIM_PARSER(file) { DECLARE_ANIM_PARSER(position) { debugC(7, kDebugParser, "ANIM_PARSER(position) "); - ctxt.a->_left = atoi(_tokens[1]); - ctxt.a->_top = atoi(_tokens[2]); - ctxt.a->_z = atoi(_tokens[3]); + ctxt.a->setX(atoi(_tokens[1])); + ctxt.a->setY(atoi(_tokens[2])); + ctxt.a->setZ(atoi(_tokens[3])); } @@ -284,10 +281,6 @@ DECLARE_ANIM_PARSER(moveto) { DECLARE_ANIM_PARSER(endanimation) { debugC(7, kDebugParser, "ANIM_PARSER(endanimation) "); - - ctxt.a->_oldPos.x = -1000; - ctxt.a->_oldPos.y = -1000; - ctxt.a->_flags |= 0x1000000; _parser->popTables(); @@ -523,7 +516,7 @@ DECLARE_INSTRUCTION_PARSER(defLocal) { } ctxt.inst->_opA.setLocal(&ctxt.locals[index]); - ctxt.inst->_opB.setImmediate(ctxt.locals[index]._value); + ctxt.inst->_opB.setImmediate(ctxt.locals[index].getValue()); ctxt.inst->_index = INST_SET; } @@ -558,16 +551,16 @@ void ProgramParser_ns::parseRValue(ScriptVar &v, const char *str) { } if (str[0] == 'X') { - v.setField(&a->_left); + v.setField(a.get(), &Animation::getX); } else if (str[0] == 'Y') { - v.setField(&a->_top); + v.setField(a.get(), &Animation::getY); } else if (str[0] == 'Z') { - v.setField(&a->_z); + v.setField(a.get(), &Animation::getZ); } else if (str[0] == 'F') { - v.setField(&a->_frame); + v.setField(a.get(), &Animation::getF); } } @@ -588,16 +581,16 @@ void ProgramParser_ns::parseLValue(ScriptVar &v, const char *str) { } if (str[0] == 'X') { - v.setField(&a->_left); + v.setField(a.get(), &Animation::getX, &Animation::setX); } else if (str[0] == 'Y') { - v.setField(&a->_top); + v.setField(a.get(), &Animation::getY, &Animation::setY); } else if (str[0] == 'Z') { - v.setField(&a->_z); + v.setField(a.get(), &Animation::getZ, &Animation::setZ); } else if (str[0] == 'F') { - v.setField(&a->_frame); + v.setField(a.get(), &Animation::getF, &Animation::setF); } } @@ -608,7 +601,7 @@ DECLARE_COMMAND_PARSER(flags) { createCommand(_parser->_lookup); - if (_vm->_globalTable->lookup(_tokens[1]) == Table::notFound) { + if (_vm->_globalFlagsNames->lookup(_tokens[1]) == Table::notFound) { do { char _al = _vm->_localFlagNames->lookup(_tokens[ctxt.nextToken]); ctxt.nextToken++; @@ -618,7 +611,7 @@ DECLARE_COMMAND_PARSER(flags) { } else { ctxt.cmd->u._flags |= kFlagsGlobal; do { - char _al = _vm->_globalTable->lookup(_tokens[1]); + char _al = _vm->_globalFlagsNames->lookup(_tokens[1]); ctxt.nextToken++; ctxt.cmd->u._flags |= 1 << (_al - 1); } while (!scumm_stricmp(_tokens[ctxt.nextToken++], "|")); @@ -759,11 +752,11 @@ void LocationParser_ns::parseCommandFlags() { cmd->_flagsOn |= kFlagsEnter; } else if (!scumm_strnicmp(_tokens[_si], "no", 2)) { - byte _al = _vm->_globalTable->lookup(&_tokens[_si][2]); + byte _al = _vm->_globalFlagsNames->lookup(&_tokens[_si][2]); assert(_al != Table::notFound); cmd->_flagsOff |= 1 << (_al - 1); } else { - byte _al = _vm->_globalTable->lookup(_tokens[_si]); + byte _al = _vm->_globalFlagsNames->lookup(_tokens[_si]); assert(_al != Table::notFound); cmd->_flagsOn |= 1 << (_al - 1); } @@ -880,7 +873,7 @@ Answer *LocationParser_ns::parseAnswer() { if (!scumm_stricmp(_tokens[1], "global")) { token = 2; - flagNames = _vm->_globalTable; + flagNames = _vm->_globalFlagsNames; answer->_yesFlags |= kFlagsGlobal; } else { token = 1; @@ -992,12 +985,12 @@ DECLARE_LOCATION_PARSER(location) { _vm->switchBackground(_vm->_location._name, mask); if (_tokens[2][0] != '\0') { - _vm->_char._ani->_left = atoi(_tokens[2]); - _vm->_char._ani->_top = atoi(_tokens[3]); + _vm->_char._ani->setX(atoi(_tokens[2])); + _vm->_char._ani->setY(atoi(_tokens[3])); } if (_tokens[4][0] != '\0') { - _vm->_char._ani->_frame = atoi(_tokens[4]); + _vm->_char._ani->setF(atoi(_tokens[4])); } } @@ -1316,11 +1309,7 @@ DECLARE_ZONE_PARSER(endzone) { DECLARE_ZONE_PARSER(limits) { debugC(7, kDebugParser, "ZONE_PARSER(limits) "); - - ctxt.z->_left = atoi(_tokens[1]); - ctxt.z->_top = atoi(_tokens[2]); - ctxt.z->_right = atoi(_tokens[3]); - ctxt.z->_bottom = atoi(_tokens[4]); + ctxt.z->setBox(atoi(_tokens[1]), atoi(_tokens[2]), atoi(_tokens[3]), atoi(_tokens[4])); } @@ -1411,8 +1400,8 @@ void LocationParser_ns::parseGetData(ZonePtr z) { GfxObj *obj = _vm->_gfx->loadGet(_tokens[1]); obj->frame = 0; - obj->x = z->_left; - obj->y = z->_top; + obj->x = z->getX(); + obj->y = z->getY(); _vm->_gfx->showGfxObj(obj, visible); data->gfxobj = obj; @@ -1474,8 +1463,8 @@ void LocationParser_ns::parseDoorData(ZonePtr z) { GfxObj *obj = _vm->_gfx->loadDoor(_tokens[1]); obj->frame = frame; - obj->x = z->_left; - obj->y = z->_top; + obj->x = z->getX(); + obj->y = z->getY(); _vm->_gfx->showGfxObj(obj, true); data->gfxobj = obj; diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp index 87a556182e..44613c970c 100644 --- a/engines/parallaction/saveload.cpp +++ b/engines/parallaction/saveload.cpp @@ -125,7 +125,7 @@ void Parallaction_ns::doLoadGame(uint16 slot) { _score = atoi(s); f->readLine(s, 15); - _commandFlags = atoi(s); + _globalFlags = atoi(s); f->readLine(s, 15); @@ -207,13 +207,13 @@ void Parallaction_ns::doSaveGame(uint16 slot, const char* name) { sprintf(s, "%s\n", _saveData1); f->writeString(s); - sprintf(s, "%d\n", _char._ani->_left); + sprintf(s, "%d\n", _char._ani->getX()); f->writeString(s); - sprintf(s, "%d\n", _char._ani->_top); + sprintf(s, "%d\n", _char._ani->getY()); f->writeString(s); sprintf(s, "%d\n", _score); f->writeString(s); - sprintf(s, "%u\n", _commandFlags); + sprintf(s, "%u\n", _globalFlags); f->writeString(s); sprintf(s, "%d\n", _numLocations); diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp index bf8f423fd5..5fd67d26dc 100644 --- a/engines/parallaction/walk.cpp +++ b/engines/parallaction/walk.cpp @@ -479,7 +479,7 @@ void PathWalker_BR::finalizeWalk() { _char.setFoot(foot); #endif - _ch->_ani->_frame = _dirFrame; // temporary solution + _ch->_ani->setF(_dirFrame); // temporary solution #if 0 // TODO: support scrolling ;) @@ -515,7 +515,7 @@ void PathWalker_BR::walk() { GfxObj *obj = _ch->_ani->gfxobj; Common::Rect rect; - obj->getRect(_ch->_ani->_frame, rect); + obj->getRect(_ch->_ani->getF(), rect); uint scale; if (rect.bottom > _vm->_location._zeta0) { @@ -609,11 +609,11 @@ void PathWalker_BR::walk() { if (_fieldC) { debugC(9, kDebugWalk, "PathWalker_BR::walk, foot moved from (%i, %i) to (%i, %i)\n", _startFoot.x, _startFoot.y, newpos.x, newpos.y); - _ch->_ani->_frame = walkFrame + _dirFrame + 1; + _ch->_ani->setF(walkFrame + _dirFrame + 1); _startFoot.x = newpos.x; _startFoot.y = newpos.y; _ch->setFoot(_startFoot); - _ch->_ani->_z = newpos.y; + _ch->_ani->setZ(newpos.y); } if (_fieldC || !_ch->_walkPath.empty()) { diff --git a/engines/queen/queen.h b/engines/queen/queen.h index 0beb557b36..1eea43e882 100644 --- a/engines/queen/queen.h +++ b/engines/queen/queen.h @@ -29,7 +29,7 @@ #include "engines/engine.h" namespace Common { - class InSaveFile; + class SeekableReadStream; } #if defined(_WIN32_WCE) && (_WIN32_WCE <= 300) @@ -112,7 +112,7 @@ public: void makeGameStateName(int slot, char *buf) const; int getGameStateSlot(const char *filename) const; void findGameStateDescriptions(char descriptions[100][32]); - Common::InSaveFile *readGameStateHeader(int slot, GameStateHeader *gsh); + Common::SeekableReadStream *readGameStateHeader(int slot, GameStateHeader *gsh); enum { SAVESTATE_CUR_VER = 1, diff --git a/engines/saga/displayinfo.h b/engines/saga/displayinfo.h index 7fee5fbee1..3775e904ff 100644 --- a/engines/saga/displayinfo.h +++ b/engines/saga/displayinfo.h @@ -348,9 +348,9 @@ static PanelButton IHNM_QuitPanelButtons[] = { }; static PanelButton IHNM_LoadPanelButtons[] = { - // TODO - {kPanelButtonLoad, 101,19, 60,16, kTextOK,'o',0, 0,0,0}, - {kPanelButtonLoadText, -1,5, 0,0, kTextLoadSuccessful,'-',0, 0,0,0}, + {kPanelButtonLoad, 26,80, 80,25, kTextOK,'o',0, 0,0,0}, + {kPanelButtonLoad, 156,80, 80,25, kTextCancel,'c',0, 0,0,0}, + {kPanelButtonLoadText, -1,30, 0,0, kTextLoadSavedGame,'-',0, 0,0,0}, }; static PanelButton IHNM_SavePanelButtons[] = { diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index 1d048baaad..256e231f57 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -94,7 +94,7 @@ static int IHNMTextStringIdsLUT[56] = { 8, // Give 10, // Options 11, // Test - 12, // + 12, // Demo 13, // Help 14, // Quit Game 16, // Fast @@ -905,10 +905,13 @@ void Interface::drawPanelText(Surface *ds, InterfacePanel *panel, PanelButton *p textFont = kKnownFontMedium; textShadowKnownColor = kKnownColorVerbTextShadow; } else { - if (panelButton->id < 39 || panelButton->id > 50) { + if ((panelButton->id < 39 || panelButton->id > 50) && panelButton->id != kTextLoadSavedGame) { // Read non-hardcoded strings from the LUT string table, loaded from the game // data files text = _vm->_script->_mainStrings.getString(IHNMTextStringIdsLUT[panelButton->id]); + } else if (panelButton->id == kTextLoadSavedGame) { + // a bit of a kludge, but it will do + text = _vm->getTextString(52); } else { // Hardcoded strings in IHNM are read from the ITE hardcoded strings text = _vm->getTextString(panelButton->id); @@ -1142,7 +1145,21 @@ void Interface::setLoad(PanelButton *panelButton) { _loadPanel.currentButton = NULL; switch (panelButton->id) { case kTextOK: - setMode(kPanelMain); + if (_vm->getGameType() == GType_ITE) { + setMode(kPanelMain); + } else { + if (_vm->getSaveFilesCount() > 0) { + if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) { + debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); + setMode(kPanelMain); + _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber)); + } + } + } + break; + case kTextCancel: + // IHNM only + setMode(kPanelOption); break; } } @@ -1573,7 +1590,6 @@ void Interface::handleChapterSelectionClick(const Point& mousePoint) { } void Interface::setOption(PanelButton *panelButton) { - char * fileName; _optionPanel.currentButton = NULL; switch (panelButton->id) { case kTextContinuePlaying: @@ -1594,13 +1610,16 @@ void Interface::setOption(PanelButton *panelButton) { setMode(kPanelQuit); break; case kTextLoad: - if (_vm->getSaveFilesCount() > 0) { - if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) { - debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); - fileName = _vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); - setMode(kPanelMain); - _vm->load(fileName); + if (_vm->getGameType() == GType_ITE) { + if (_vm->getSaveFilesCount() > 0) { + if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) { + debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); + setMode(kPanelMain); + _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber)); + } } + } else { + setMode(kPanelLoad); } break; case kTextSave: diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp index 43c3d21012..bbd5cbb615 100644 --- a/engines/saga/itedata.cpp +++ b/engines/saga/itedata.cpp @@ -339,7 +339,7 @@ FxTable ITE_SfxTable[ITE_SFXCOUNT] = { { 73, 64 } }; -const char *ITEinterfaceTextStrings[][52] = { +const char *ITEinterfaceTextStrings[][53] = { { // Note that the "Load Successful!" string is never used in ScummVM "Walk to", "Look At", "Pick Up", "Talk to", "Open", @@ -358,7 +358,8 @@ const char *ITEinterfaceTextStrings[][52] = { "There's no opening to close.", "I don't know how to do that.", "Show Dialog", - "What is Rif's reply?" + "What is Rif's reply?", + "Loading a saved game" }, // German { @@ -378,7 +379,8 @@ const char *ITEinterfaceTextStrings[][52] = { "Hier ist keine \231ffnung zum Schlie$en.", "Ich wei$ nicht, wie ich das machen soll.", "Text zeigen", - "Wie lautet die Antwort?" + "Wie lautet die Antwort?", + "Spielstand wird geladen" }, // Italian fan translation { @@ -398,7 +400,8 @@ const char *ITEinterfaceTextStrings[][52] = { "Nessuna apertura da chiudere.", "Non saprei come farlo.", "Dialoghi", - "Come risponderebbe Rif?" + "Come risponderebbe Rif?", + "Vuoi davvero caricare il gioco?" }, // Spanish IHNM { @@ -420,7 +423,8 @@ const char *ITEinterfaceTextStrings[][52] = { NULL, NULL, NULL, - NULL + NULL, + "Cardango una partida guardada" } }; diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h index 00efd070c1..6d0f5a9d70 100644 --- a/engines/saga/itedata.h +++ b/engines/saga/itedata.h @@ -88,7 +88,7 @@ struct FxTable { extern ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT]; extern FxTable ITE_SfxTable[ITE_SFXCOUNT]; -extern const char *ITEinterfaceTextStrings[][52]; +extern const char *ITEinterfaceTextStrings[][53]; #define PUZZLE_PIECES 15 diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 6b6eb6b3fb..eeb8f7a61b 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -295,7 +295,8 @@ enum TextStringIds { kTextVoices, kTextText, kTextAudio, - kTextBoth + kTextBoth, + kTextLoadSavedGame }; struct GameResourceDescription { diff --git a/engines/saga/sprite.cpp b/engines/saga/sprite.cpp index be4f2a423d..d9c7b446ba 100644 --- a/engines/saga/sprite.cpp +++ b/engines/saga/sprite.cpp @@ -74,9 +74,11 @@ Sprite::Sprite(SagaEngine *vm) : _vm(vm) { Sprite::~Sprite(void) { debug(8, "Shutting down sprite subsystem..."); _mainSprites.freeMem(); - _inventorySprites.freeMem(); - _arrowSprites.freeMem(); - _saveReminderSprites.freeMem(); + if (_vm->getGameType() == GType_IHNM) { + _inventorySprites.freeMem(); + _arrowSprites.freeMem(); + _saveReminderSprites.freeMem(); + } free(_decodeBuf); } diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp index 0fa8b579ca..1e632a034e 100644 --- a/engines/scumm/boxes.cpp +++ b/engines/scumm/boxes.cpp @@ -544,8 +544,8 @@ bool ScummEngine::checkXYInBoxBounds(int boxnum, int x, int y) { // Corner case: If the box is a simple line segment, we consider the // point to be contained "in" (or rather, lying on) the line if it // is very close to its projection to the line segment. - if (box.ul == box.ur && box.lr == box.ll || - box.ul == box.ll && box.ur == box.lr) { + if ((box.ul == box.ur && box.lr == box.ll) || + (box.ul == box.ll && box.ur == box.lr)) { Common::Point tmp; tmp = closestPtOnLine(box.ul, box.lr, p); @@ -803,8 +803,8 @@ bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, Common::Point } if (box1.ul.y > box2.ur.y || box2.ul.y > box1.ur.y || - (box1.ur.y == box2.ul.y || box2.ur.y == box1.ul.y) && - box1.ul.y != box1.ur.y && box2.ul.y != box2.ur.y) { + ((box1.ur.y == box2.ul.y || box2.ur.y == box1.ul.y) && + box1.ul.y != box1.ur.y && box2.ul.y != box2.ur.y)) { if (flag & 1) SWAP(box1.ul.y, box1.ur.y); if (flag & 2) @@ -858,8 +858,8 @@ bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, Common::Point } if (box1.ul.x > box2.ur.x || box2.ul.x > box1.ur.x || - (box1.ur.x == box2.ul.x || box2.ur.x == box1.ul.x) && - box1.ul.x != box1.ur.x && box2.ul.x != box2.ur.x) { + ((box1.ur.x == box2.ul.x || box2.ur.x == box1.ul.x) && + box1.ul.x != box1.ur.x && box2.ul.x != box2.ur.x)) { if (flag & 1) SWAP(box1.ul.x, box1.ur.x); if (flag & 2) @@ -1074,8 +1074,8 @@ bool ScummEngine::areBoxesNeighbours(int box1nr, int box2nr) { } if (box.ur.y < box2.ul.y || box.ul.y > box2.ur.y || - (box.ul.y == box2.ur.y || - box.ur.y == box2.ul.y) && box2.ur.y != box2.ul.y && box.ul.y != box.ur.y) { + ((box.ul.y == box2.ur.y || + box.ur.y == box2.ul.y) && box2.ur.y != box2.ul.y && box.ul.y != box.ur.y)) { } else { return true; } @@ -1103,8 +1103,8 @@ bool ScummEngine::areBoxesNeighbours(int box1nr, int box2nr) { } if (box.ur.x < box2.ul.x || box.ul.x > box2.ur.x || - (box.ul.x == box2.ur.x || - box.ur.x == box2.ul.x) && box2.ur.x != box2.ul.x && box.ul.x != box.ur.x) { + ((box.ul.x == box2.ur.x || + box.ur.x == box2.ul.x) && box2.ur.x != box2.ul.x && box.ul.x != box.ur.x)) { } else { return true; diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 5a45fb7da9..609aca996d 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -49,7 +49,7 @@ void ScummEngine::loadCJKFont() { Common::File fp; _useCJKMode = false; _textSurfaceMultiplier = 1; - _newLineCharacter = 0xfe; + _newLineCharacter = 0; if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji int numChar = 256 * 32; @@ -277,7 +277,7 @@ CharsetRenderer::CharsetRenderer(ScummEngine *vm) { _disableOffsX = false; _vm = vm; - _curId = 0; + _curId = -1; } CharsetRenderer::~CharsetRenderer() { @@ -289,7 +289,10 @@ CharsetRendererCommon::CharsetRendererCommon(ScummEngine *vm) _shadowColor = 0; } -void CharsetRendererCommon::setCurID(byte id) { +void CharsetRendererCommon::setCurID(int32 id) { + if (id == -1) + return; + assertRange(0, id, _vm->_numCharsets - 1, "charset"); _curId = id; @@ -308,7 +311,10 @@ void CharsetRendererCommon::setCurID(byte id) { _numChars = READ_LE_UINT16(_fontPtr + 2); } -void CharsetRendererV3::setCurID(byte id) { +void CharsetRendererV3::setCurID(int32 id) { + if (id == -1) + return; + assertRange(0, id, _vm->_numCharsets - 1, "charset"); _curId = id; @@ -668,7 +674,8 @@ void CharsetRenderer::translateColor() { void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) { static const SaveLoadEntry charsetRendererEntries[] = { - MKLINE(CharsetRenderer, _curId, sleByte, VER(73)), + MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)), + MKLINE(CharsetRenderer, _curId, sleInt32, VER(74)), MKLINE(CharsetRenderer, _color, sleByte, VER(73)), MKEND() }; @@ -988,7 +995,10 @@ CharsetRendererNut::~CharsetRendererNut() { } } -void CharsetRendererNut::setCurID(byte id) { +void CharsetRendererNut::setCurID(int32 id) { + if (id == -1) + return; + int numFonts = ((_vm->_game.id == GID_CMI) && (_vm->_game.features & GF_DEMO)) ? 4 : 5; assert(id < numFonts); _curId = id; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index b62dbc6006..dbe02fc8fc 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -67,7 +67,7 @@ public: protected: ScummEngine *_vm; - byte _curId; + int32 _curId; public: CharsetRenderer(ScummEngine *vm); @@ -80,7 +80,7 @@ public: void addLinebreaks(int a, byte *str, int pos, int maxwidth); void translateColor(); - virtual void setCurID(byte id) = 0; + virtual void setCurID(int32 id) = 0; int getCurID() { return _curId; } virtual int getFontHeight() = 0; @@ -113,7 +113,7 @@ protected: public: CharsetRendererCommon(ScummEngine *vm); - void setCurID(byte id); + void setCurID(int32 id); int getFontHeight(); }; @@ -142,7 +142,7 @@ protected: public: CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {} - void setCurID(byte id) {} + void setCurID(int32 id) {} void printChar(int chr, bool ignoreCharsetMask); void drawChar(int chr, const Graphics::Surface &s, int x, int y); @@ -159,7 +159,7 @@ public: void printChar(int chr, bool ignoreCharsetMask); void drawChar(int chr, const Graphics::Surface &s, int x, int y); - void setCurID(byte id); + void setCurID(int32 id); void setColor(byte color); int getCharWidth(byte chr); }; @@ -168,7 +168,7 @@ class CharsetRendererV2 : public CharsetRendererV3 { public: CharsetRendererV2(ScummEngine *vm, Common::Language language); - void setCurID(byte id) {} + void setCurID(int32 id) {} int getCharWidth(byte chr) { return 8; } }; @@ -184,7 +184,7 @@ public: void printChar(int chr, bool ignoreCharsetMask); - void setCurID(byte id); + void setCurID(int32 id); int getFontHeight(); int getCharHeight(byte chr); diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 68d3010199..ca5dba7865 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -820,9 +820,10 @@ PluginError ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) cons // unknown MD5, or with a medium debug level in case of a known MD5 (for // debugging purposes). if (!findInMD5Table(res.md5.c_str())) { - printf("Your game version appears to be unknown. Please, report the following\n"); - printf("data to the ScummVM team along with name of the game you tried to add\n"); - printf("and its version/language/etc.:\n"); + printf("Your game version appears to be unknown. If this is *NOT* a fan-modified\n"); + printf("version (in particular, not a fan-made translation), please, report the\n"); + printf("following data to the ScummVM team along with name of the game you tried\n"); + printf("to add and its version/language/etc.:\n"); printf(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", res.game.gameid, diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 8d977719da..8886214910 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -367,37 +367,32 @@ void SaveLoadChooser::reflowLayout() { void SaveLoadChooser::updateInfos(bool redraw) { int selItem = _list->getSelected(); - Graphics::Surface *thumb; - thumb = _vm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem); + Graphics::Surface *thumb = 0; + if (selItem >= 0 && !_list->getSelectedString().empty()) + thumb = _vm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem); if (thumb) { _gfxWidget->setGfx(thumb); _gfxWidget->useAlpha(256); thumb->free(); + delete thumb; } else { _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); } - delete thumb; - if (redraw) - _gfxWidget->draw(); - InfoStuff infos; memset(&infos, 0, sizeof(InfoStuff)); - char buffer[32]; - if (_vm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) { + if (selItem >= 0 && !_list->getSelectedString().empty() + && _vm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) { + char buffer[32]; snprintf(buffer, 32, "Date: %.2d.%.2d.%.4d", (infos.date >> 24) & 0xFF, (infos.date >> 16) & 0xFF, infos.date & 0xFFFF); _date->setLabel(buffer); - if (redraw) - _date->draw(); snprintf(buffer, 32, "Time: %.2d:%.2d", (infos.time >> 8) & 0xFF, infos.time & 0xFF); _time->setLabel(buffer); - if (redraw) - _time->draw(); int minutes = infos.playtime / 60; int hours = minutes / 60; @@ -406,23 +401,17 @@ void SaveLoadChooser::updateInfos(bool redraw) { snprintf(buffer, 32, "Playtime: %.2d:%.2d", hours & 0xFF, minutes & 0xFF); _playtime->setLabel(buffer); - if (redraw) - _playtime->draw(); } else { - snprintf(buffer, 32, "No date saved"); - _date->setLabel(buffer); - if (redraw) - _date->draw(); - - snprintf(buffer, 32, "No time saved"); - _time->setLabel(buffer); - if (redraw) - _time->draw(); + _date->setLabel("No date saved"); + _time->setLabel("No time saved"); + _playtime->setLabel("No playtime saved"); + } - snprintf(buffer, 32, "No playtime saved"); - _playtime->setLabel(buffer); - if (redraw) - _playtime->draw(); + if (redraw) { + _gfxWidget->draw(); + _date->draw(); + _time->draw(); + _playtime->draw(); } } diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index 6c8d24d25a..d09accafb0 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -669,7 +669,7 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i x += 16; while (x + width >= _screenWidth) width -= 16; - if (width < 0) + if (width <= 0) return; } diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index f9e4eb415c..54dfca9eea 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -479,6 +479,9 @@ Graphics::Surface *ScummEngine::loadThumbnailFromSlot(int slot) { Common::SeekableReadStream *in; SaveGameHeader hdr; + if (slot < 0) + return 0; + makeSavegameName(filename, slot, false); if (!(in = _saveFileMan->openForLoading(filename))) { return 0; @@ -507,6 +510,9 @@ bool ScummEngine::loadInfosFromSlot(int slot, InfoStuff *stuff) { Common::SeekableReadStream *in; SaveGameHeader hdr; + if (slot < 0) + return 0; + makeSavegameName(filename, slot, false); if (!(in = _saveFileMan->openForLoading(filename))) { return false; @@ -595,7 +601,7 @@ bool ScummEngine::loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff) return true; } -void ScummEngine::saveInfos(Common::OutSaveFile* file) { +void ScummEngine::saveInfos(Common::WriteStream* file) { SaveInfoSection section; section.type = MKID_BE('INFO'); section.version = INFOSECTION_VERSION; diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index 0ddb4e5d2a..2d7ee64680 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -31,7 +31,7 @@ namespace Common { class SeekableReadStream; - class OutSaveFile; + class WriteStream; } namespace Scumm { @@ -50,7 +50,7 @@ namespace Scumm { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 73 +#define CURRENT_VER 74 /** * An auxillary macro, used to specify savegame versions. We use this instead @@ -125,7 +125,7 @@ struct SaveLoadEntry { class Serializer { public: - Serializer(Common::SeekableReadStream *in, Common::OutSaveFile *out, uint32 savegameVersion) + Serializer(Common::SeekableReadStream *in, Common::WriteStream *out, uint32 savegameVersion) : _loadStream(in), _saveStream(out), _savegameVersion(savegameVersion) { } @@ -151,7 +151,7 @@ public: protected: Common::SeekableReadStream *_loadStream; - Common::OutSaveFile *_saveStream; + Common::WriteStream *_saveStream; uint32 _savegameVersion; void saveArrayOf(void *b, int len, int datasize, byte filetype); diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index 9268768f2e..c727b59c64 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -769,14 +769,14 @@ void ScummEngine::runInventoryScript(int i) { args[0] = i; if (VAR(VAR_INVENTORY_SCRIPT)) { if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) { - inventoryScript(); + inventoryScriptIndy3Mac(); } else { runScript(VAR(VAR_INVENTORY_SCRIPT), 0, 0, args); } } } -void ScummEngine::inventoryScript() { +void ScummEngine::inventoryScriptIndy3Mac() { VerbSlot *vs; int args[24]; int j, slot; @@ -1201,11 +1201,11 @@ void ScummEngine::runInputScript(int clickArea, int val, int mode) { if (clickArea == kVerbClickArea && (val >= 101 && val <= 108)) { if (val == 107) { VAR(67) -= 2; - inventoryScript(); + inventoryScriptIndy3Mac(); return; } else if (val == 108) { VAR(67) += 2; - inventoryScript(); + inventoryScriptIndy3Mac(); return; } else { args[0] = 3; diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index 431321f459..3cbb2b8266 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -1402,7 +1402,7 @@ void ScummEngine_v5::o5_ifClassOfIs() { while ((_opcode = fetchScriptByte()) != 0xFF) { cls = getVarOrDirectWord(PARAM_1); b = getClass(act, cls); - if (cls & 0x80 && !b || !(cls & 0x80) && b) + if (((cls & 0x80) && !b) || (!(cls & 0x80) && b)) cond = false; } if (cond) diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp index 04ea53137b..b9a00e6d38 100644 --- a/engines/scumm/script_v6.cpp +++ b/engines/scumm/script_v6.cpp @@ -2390,6 +2390,15 @@ void ScummEngine_v6::o6_talkActor() { _actorToPrintStrFor = pop(); + // WORKAROUND for bug #2016521: "DOTT: Bernard impersonating LaVerne" + // Original script did not check for VAR_EGO == 2 before executing + // a talkActor opcode. + if (_game.id == GID_TENTACLE && vm.slot[_currentScript].number == 307 + && VAR(VAR_EGO) != 2 && _actorToPrintStrFor == 2) { + _scriptPointer += resStrLen(_scriptPointer) + 1; + return; + } + _string[0].loadDefault(); actorTalk(_scriptPointer); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 20824ffe74..2763331420 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -46,7 +46,7 @@ namespace GUI { using GUI::Dialog; namespace Common { class SeekableReadStream; - class OutSaveFile; + class WriteStream; } namespace Scumm { @@ -560,7 +560,7 @@ protected: public: int _numLocalScripts, _numImages, _numRooms, _numScripts, _numSounds; // Used by HE games int _numCostumes; // FIXME - should be protected, used by Actor::remapActorPalette - int _numCharsets; // FIXME - should be protected, used by CharsetRenderer + int32 _numCharsets; // FIXME - should be protected, used by CharsetRenderer BaseCostumeLoader *_costumeLoader; BaseCostumeRenderer *_costumeRenderer; @@ -637,8 +637,8 @@ public: protected: Graphics::Surface *loadThumbnail(Common::SeekableReadStream *file); bool loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff); - void saveThumbnail(Common::OutSaveFile *file); - void saveInfos(Common::OutSaveFile* file); + void saveThumbnail(Common::WriteStream *file); + void saveInfos(Common::WriteStream* file); int32 _engineStartTime; int32 _pauseStartTime; @@ -673,7 +673,7 @@ protected: void executeScript(); void updateScriptPtr(); virtual void runInventoryScript(int i); - void inventoryScript(); + void inventoryScriptIndy3Mac(); void checkAndRunSentenceScript(); void runExitScript(); void runEntryScript(); diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index f039e2ca23..700632e4b3 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -279,7 +279,7 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) { } c = *buffer++; - if (c == _newLineCharacter) { + if (_newLineCharacter != 0 && c == _newLineCharacter) { c = 13; break; } diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp index 53169402e1..9d6b58704d 100644 --- a/engines/sky/control.cpp +++ b/engines/sky/control.cpp @@ -986,7 +986,7 @@ void Control::handleKeyPress(Common::KeyState kbd, Common::String &textBuf) { if (kbd.keycode == Common::KEYCODE_BACKSPACE) { // backspace if (textBuf.size() > 0) textBuf.deleteLastChar(); - } else { + } else if (kbd.ascii) { // Cannot enter text wider than the save/load panel if (_enteredTextWidth >= PAN_LINE_WIDTH - 10) return; diff --git a/engines/sky/logic.cpp b/engines/sky/logic.cpp index ed7e419f64..c6c6c34c4d 100644 --- a/engines/sky/logic.cpp +++ b/engines/sky/logic.cpp @@ -1774,6 +1774,7 @@ bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) { uint32 size = ((dataFileHeader *)data)->s_height * ((dataFileHeader *)data)->s_width; uint32 index = 0; uint32 width = ((dataFileHeader *)data)->s_width; + uint32 height = ((dataFileHeader *)data)->s_height; data += sizeof(dataFileHeader); @@ -1794,7 +1795,7 @@ bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) { textCompact->xcood = TOP_LEFT_X; // set coordinates textCompact->ycood = ycood; - ycood += 12; + ycood += height; } if (p == _scriptVariables + TEXT1) diff --git a/engines/sky/sound.cpp b/engines/sky/sound.cpp index 928221a9a5..f15038c0b6 100644 --- a/engines/sky/sound.cpp +++ b/engines/sky/sound.cpp @@ -1025,6 +1025,7 @@ Sound::Sound(Audio::Mixer *mixer, Disk *pDisk, uint8 pVolume) { _mixer = mixer; _saveSounds[0] = _saveSounds[1] = 0xFFFF; _mainSfxVolume = pVolume; + _isPaused = false; } Sound::~Sound(void) { @@ -1254,14 +1255,20 @@ bool Sound::startSpeech(uint16 textNum) { void Sound::fnPauseFx(void) { - _mixer->pauseID(SOUND_CH0, true); - _mixer->pauseID(SOUND_CH1, true); + if (!_isPaused) { + _isPaused = true; + _mixer->pauseID(SOUND_CH0, true); + _mixer->pauseID(SOUND_CH1, true); + } } void Sound::fnUnPauseFx(void) { - _mixer->pauseID(SOUND_CH0, false); - _mixer->pauseID(SOUND_CH1, false); + if (_isPaused) { + _isPaused = false; + _mixer->pauseID(SOUND_CH0, false); + _mixer->pauseID(SOUND_CH1, false); + } } } // End of namespace Sky diff --git a/engines/sky/sound.h b/engines/sky/sound.h index 28e2e8c88a..0ad509700e 100644 --- a/engines/sky/sound.h +++ b/engines/sky/sound.h @@ -89,6 +89,8 @@ private: uint8 *_sampleRates, *_sfxInfo; uint8 _mainSfxVolume; + bool _isPaused; + static uint16 _speechConvertTable[8]; static SfxQueue _sfxQueue[MAX_QUEUED_FX]; }; diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index a638dde2c5..7da4192456 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -76,13 +76,28 @@ namespace Tinsel { static const TinselGameDescription gameDescriptions[] = { - // Note: versions with *.gra files use tinsel v1 (28/2/1995), whereas - // versions with *.scn files tinsel v2 (7/5/1995) - // Update: this is not entirely true, there were some versions released - // with *.gra files and used tinsel v2 + // The DW1 demo was based on an older revision of the Tinsel engine + // than the one used in the released game. We call it Tinsel v0 as + // opposed to v1 which was used in the full retail version of DW. + + { // Demo from http://www.adventure-treff.de/specials/dl_demos.php + { + "dw", + "Demo", + AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192), + //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308), + Common::EN_ANY, + Common::kPlatformPC, + Common::ADGF_DEMO + }, + GID_DW1, + 0, + GF_DEMO, + TINSEL_V0, + }, { - { // This version has *.gra files but uses tinsel v2 + { // This version has *.gra files "dw", "Floppy", AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656), @@ -93,10 +108,10 @@ static const TinselGameDescription gameDescriptions[] = { GID_DW1, 0, GF_FLOPPY, - TINSEL_V2, + TINSEL_V1, }, - { // English CD v1. This version has *.gra files but uses tinsel v2 + { // English CD. This version has *.gra files { "dw", "CD", @@ -112,10 +127,10 @@ static const TinselGameDescription gameDescriptions[] = { GID_DW1, 0, GF_CD, - TINSEL_V2, + TINSEL_V1, }, - { // English CD v2 + { // English CD with SCN files { "dw", "CD", @@ -131,11 +146,11 @@ static const TinselGameDescription gameDescriptions[] = { GID_DW1, 0, GF_CD | GF_SCNFILES, - TINSEL_V2, + TINSEL_V1, }, #if 0 - { // English Saturn CD + { // English Saturn CD. Not (yet?) supported { "dw", "CD", @@ -151,25 +166,9 @@ static const TinselGameDescription gameDescriptions[] = { GID_DW1, 0, GF_CD, - TINSEL_V2, - }, -#endif - - { // Demo from http://www.adventure-treff.de/specials/dl_demos.php - { - "dw", - "Demo", - AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192), - //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308), - Common::EN_ANY, - Common::kPlatformPC, - Common::ADGF_DEMO - }, - GID_DW1, - 0, - GF_DEMO, TINSEL_V1, }, +#endif { // German CD re-release "Neon Edition" // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT @@ -184,9 +183,9 @@ static const TinselGameDescription gameDescriptions[] = { GID_DW1, 0, GF_CD | GF_SCNFILES, - TINSEL_V2, + TINSEL_V1, }, - + { AD_TABLE_END_MARKER, 0, 0, 0, 0 } }; diff --git a/engines/tinsel/scn.cpp b/engines/tinsel/scn.cpp index b14b1c5962..8639979b41 100644 --- a/engines/tinsel/scn.cpp +++ b/engines/tinsel/scn.cpp @@ -50,7 +50,7 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) { // V1 chunk types can be found by substracting 2 from the // chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are // the same in V1 and V2 - if (_vm->getVersion() == TINSEL_V1 && + if (_vm->getVersion() == TINSEL_V0 && chunk != CHUNK_STRING && chunk != CHUNK_BITMAP) chunk -= 0x2L; diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h index 9ffadfe8c1..44cc83af9b 100644 --- a/engines/tinsel/tinsel.h +++ b/engines/tinsel/tinsel.h @@ -59,8 +59,8 @@ enum TinselGameFeatures { }; enum TinselEngineVersion { - TINSEL_V1 = 1 << 0, - TINSEL_V2 = 1 << 1 + TINSEL_V0 = 1 << 0, // Used in the DW1 demo only + TINSEL_V1 = 1 << 1 }; struct TinselGameDescription; diff --git a/engines/touche/graphics.cpp b/engines/touche/graphics.cpp index 999aa8005c..ab711beba0 100644 --- a/engines/touche/graphics.cpp +++ b/engines/touche/graphics.cpp @@ -76,10 +76,13 @@ int Graphics::getCharWidth16(uint8 chr) { return chrData[2]; } -void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str) { +void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax) { while (*str) { uint8 chr = (uint8)*str++; x += drawChar16(dst, dstPitch, chr, x, y, color); + if (xmax != 0 && x > xmax) { + break; + } } } diff --git a/engines/touche/graphics.h b/engines/touche/graphics.h index 6b4072d896..9c928f983c 100644 --- a/engines/touche/graphics.h +++ b/engines/touche/graphics.h @@ -40,7 +40,7 @@ public: static void setupFont(Common::Language language); static int getStringWidth16(const char *str); static int getCharWidth16(uint8 chr); - static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str); + static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax = 0); static int drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color); static void fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color); static void drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2); diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp index ec9a0ea903..a377a6e45f 100644 --- a/engines/touche/staticres.cpp +++ b/engines/touche/staticres.cpp @@ -889,6 +889,7 @@ const uint8 Graphics::_freGerFontData[] = { 0xC0, 0x00, 0x00, }; +// spanish charset differs from original executable, see tracker item #2040311. const uint16 Graphics::_spaFontOffs[] = { 0x0000, 0x0007, 0x0024, 0x0043, 0x0072, 0x00AD, 0x00E0, 0x0113, 0x0124, 0x0141, 0x015E, 0x0191, 0x01C4, 0x01E3, 0x01F8, 0x0215, 0x0232, 0x0269, 0x0286, 0x02BD, @@ -904,11 +905,11 @@ const uint16 Graphics::_spaFontOffs[] = { 0x1462, 0x1499, 0x14A4, 0x14DB, 0x1512, 0x1549, 0x1580, 0x15B7, 0x15EE, 0x1625, 0x165C, 0x1667, 0x169E, 0x16D5, 0x16E0, 0x16EB, 0x1722, 0x172D, 0x1738, 0x176F, 0x178C, 0x17C3, 0x17FA, 0x1831, 0x1868, 0x1873, 0x187E, 0x18B5, 0x18C0, 0x18CB, - 0x18D6, 0x18E1, 0x18FE, 0x1929, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x18D6, 0x18E1, 0x18FE, 0x1929, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x054B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0703, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1954 }; diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp index ac8e8a786a..a39517fe32 100644 --- a/engines/touche/touche.cpp +++ b/engines/touche/touche.cpp @@ -1248,10 +1248,11 @@ int ToucheEngine::getStringWidth(int num) const { return Graphics::getStringWidth16(str); } -void ToucheEngine::drawString(uint16 color, int x, int y, int16 num) { +void ToucheEngine::drawString(uint16 color, int x, int y, int16 num, StringType strType) { + const int xmax = (_language == Common::ES_ESP && strType == kStringTypeConversation) ? kScreenWidth - 20 : 0; if (num) { const char *str = getString(num); - Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str); + Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str, xmax); } } @@ -2414,7 +2415,7 @@ void ToucheEngine::drawCharacterConversation() { } drawConversationPanel(); for (int i = 0; i < 4; ++i) { - drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg); + drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg, kStringTypeConversation); } updateScreenArea(0, 320, kScreenWidth, kScreenHeight - 320); _conversationAreaCleared = false; @@ -2422,7 +2423,7 @@ void ToucheEngine::drawCharacterConversation() { void ToucheEngine::drawConversationString(int num, uint16 color) { const int y = 328 + num * kTextHeight; - drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg); + drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg, kStringTypeConversation); updateScreenArea(0, y, kScreenWidth, kTextHeight); } diff --git a/engines/touche/touche.h b/engines/touche/touche.h index c1bc12655f..41f5c832c5 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -331,6 +331,11 @@ enum { kMaxSaveStates = 100 }; +enum StringType { + kStringTypeDefault, + kStringTypeConversation +}; + class MidiPlayer; class ToucheEngine: public Engine { @@ -399,7 +404,7 @@ protected: void setKeyCharMoney(); const char *getString(int num) const; int getStringWidth(int num) const; - void drawString(uint16 color, int x, int y, int16 num); + void drawString(uint16 color, int x, int y, int16 num, StringType strType = kStringTypeDefault); void drawGameString(uint16 color, int x1, int y, const char *str); int restartKeyCharScriptOnAction(int action, int obj1, int obj2); void buildSpriteScalingTable(int z1, int z2); |