Merge branch 'master' into phantom

This commit is contained in:
Paul Gilbert 2015-05-31 14:45:10 -04:00
commit e5296ebf8d
994 changed files with 78117 additions and 30596 deletions

16
AUTHORS
View File

@ -257,9 +257,12 @@ ScummVM Team
Wintermute:
Einar Johan T. Somaaen
Tobia Tesan
ZVision:
Adrian Astley
Filippos Karapetis
Anton Yarcev
Backend Teams
-------------
@ -523,6 +526,12 @@ Other contributions
Victor Gonzalez - Soltys Spanish translation
Alejandro Gomez de la Munoza - Soltys Spanish translation
CGE2:
Arnaud Boutonne - Sfinx English translation
Thierry Crozat - Sfinx English translation
Peter Bozso - Sfinx English translation editor
Ryan Clark - Sfinx English translation editor
Drascula:
Thierry Crozat - Improve French translation
@ -556,6 +565,7 @@ Other contributions
Jeroen Janssen - Numerous readability and bugfix patches
Keith Kaisershot - Several Pegasus Prime patches
Andreas Karlsson - Initial port for SymbianOS
Stefan Kristiansson - Initial work on SDL2 support
Claudio Matsuoka - Daily Linux builds
Thomas Mayer - PSP port contributions
Sean Murray - ScummVM tools GUI application (GSoC 2007
@ -651,7 +661,7 @@ Special thanks to
repository, planet and doxygen sites as well as tons
of HD space
DOSBox Team - For their awesome OPL2 and OPL3 emulator
Yusuke Kamiyamane - For contributing some GUI icons
Yusuke Kamiyamane - For contributing some GUI icons
Till Kresslein - For design of modern ScummVM GUI
Jezar - For his freeverb filter implementation
Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
@ -700,8 +710,8 @@ Special thanks to
of Dreamweb and for their tremendous support.
Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe
Avalon for providing full source code for Soltys and letting us
redistribute the game.
Avalon for providing full source code for Soltys and Sfinx and letting us
redistribute the games.
Jan Nedoma for providing the sources to the Wintermute-engine, and for his
support while porting the engine to ScummVM.

12
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,12 @@
Thank you for considering contributing to ScummVM.
Please make sure to read our guidelines for contributions on our
[wiki](http://wiki.scummvm.org/index.php/Developer_Central). In particular:
* [Coding style](http://wiki.scummvm.org/index.php/Code_Formatting_Conventions)
* [Portability](http://wiki.scummvm.org/index.php/Coding_Conventions)
* [Commit message style](http://wiki.scummvm.org/index.php/Commit_Guidelines)
* License: GPLv2+
If you have any questions about code, style, procedure, or anything else, feel
free to contact us on our mailing list at scummvm-devel@lists.sourceforge.net.

View File

@ -1,5 +1,5 @@
ScummVM
Copyright (C) 2001-2014 by the following:
Copyright (C) 2001-2015 by the following:
If you have contributed to this project then you deserve to be on this
list. Contact us (see: AUTHORS) and we'll add you.

View File

@ -91,6 +91,7 @@ ifeq "$(findstring config.mk,$(MAKEFILE_LIST))" "config.mk"
LDFLAGS="$(SAVED_LDFLAGS)" CXX="$(SAVED_CXX)" \
CXXFLAGS="$(SAVED_CXXFLAGS)" CPPFLAGS="$(SAVED_CPPFLAGS)" \
ASFLAGS="$(SAVED_ASFLAGS)" WINDRESFLAGS="$(SAVED_WINDRESFLAGS)" \
SDL_CONFIG="$(SAVED_SDL_CONFIG)" \
$(srcdir)/configure $(SAVED_CONFIGFLAGS)
else
$(error You need to run $(srcdir)/configure before you can run make. Check $(srcdir)/configure --help for a list of parameters)

31
NEWS
View File

@ -2,14 +2,43 @@ For a more comprehensive changelog of the latest experimental code, see:
https://github.com/scummvm/scummvm/commits/
1.8.0 (????-??-??)
New Games:
- Added support for Rex Nebular and the Cosmic Gender Bender.
- Added support for Sfinx.
- Added support for Zork Nemesis: The Forbidden Lands.
- Added support for Zork: Grand Inquisitor.
Broken Sword 1:
General:
- Updated Munt MT-32 emulation code to version 1.5.0.
AGI:
- It is now possible to disable mouse support (except for Amiga versions
and fanmade games, that require a mouse).
- Fix incorrect volume attenuation in PCjr sound code (bug #6858).
AGOS:
- Fixed arpeggio effect used in music of Amiga version of Elvira 1.
- Fixed loading and saving progress in the PC version of Waxworks.
Broken Sword 1:
- Fix speech endianness detection on big endian systems for the mac
version (bug #6720).
- Fix crash when reloading a game from the Main Menu while in the bull's
head scene (bug #6728). It may have been happening in other scenes as
well.
SCI:
- Handling of music priority has been greatly improved.
- A lot of fixes for original game script bugs that also occurred when
using the original interpreter.
KQ6 (Dual Mode), LSL5, QfG1 (EGA), QfG1 (VGA), QfG2, QfG3, SQ1, SQ4 (CD)
- Restoring from the ScummVM in-game menu should now work all the time.
- Improve support for Japanese PC-9801 games.
SCUMM:
- It is now possible to play Maniac Mansion from within Day of the
Tentacle, with a few caveats. See README for details.
1.7.0 (2014-07-21)
New Games:
- Added support for Chivalry is Not Dead.

304
README
View File

@ -15,26 +15,27 @@ Table of Contents:
* 2.1 Reporting Bugs
3.0) Supported Games
* 3.1 Copy Protection
* 3.2 Commodore64 games notes
* 3.3 Maniac Mansion NES notes
* 3.4 Macintosh games notes
* 3.5 Multi-CD games notes
* 3.6 The Curse of Monkey Island notes
* 3.7 Broken Sword games notes
* 3.8 Beneath a Steel Sky notes
* 3.9 Flight of the Amazon Queen notes
* 3.10 Gobliiins notes
* 3.11 Inherit the Earth: Quest for the Orb notes
* 3.12 Simon the Sorcerer notes
* 3.13 The Feeble Files notes
* 3.14 The Legend of Kyrandia notes
* 3.15 Sierra AGI games Predictive Input Dialog notes
* 3.16 Mickey's Space Adventure notes
* 3.17 Winnie the Pooh notes
* 3.18 Troll's Tale notes
* 3.19 Dragon History notes
* 3.20 Simultaneous speech and subtitles in Sierra SCI games
* 3.21 Known Problems
* 3.2 Day of the Tentacle notes
* 3.3 Commodore64 games notes
* 3.4 Maniac Mansion NES notes
* 3.5 Macintosh games notes
* 3.6 Multi-CD games notes
* 3.7 The Curse of Monkey Island notes
* 3.8 Broken Sword games notes
* 3.9 Beneath a Steel Sky notes
* 3.10 Flight of the Amazon Queen notes
* 3.11 Gobliiins notes
* 3.12 Inherit the Earth: Quest for the Orb notes
* 3.13 Simon the Sorcerer notes
* 3.14 The Feeble Files notes
* 3.15 The Legend of Kyrandia notes
* 3.16 Sierra AGI games Predictive Input Dialog notes
* 3.17 Mickey's Space Adventure notes
* 3.18 Winnie the Pooh notes
* 3.19 Troll's Tale notes
* 3.20 Dragon History notes
* 3.21 Simultaneous speech and subtitles in Sierra SCI games
* 3.22 Known Problems
4.0) Supported Platforms
5.0) Running ScummVM
* 5.1 Command Line Options
@ -200,7 +201,7 @@ SCUMM Games by LucasArts:
The Dig [dig]
The Curse of Monkey Island [comi]
AGI Games by Sierra:
AGI and preAGI Games by Sierra:
The Black Cauldron [bc]
Gold Rush! [goldrush]
King's Quest I [kq1]
@ -217,6 +218,9 @@ AGI Games by Sierra:
Space Quest I: The Sarien Encounter [sq1]
Space Quest II: Vohaul's Revenge [sq2]
Fanmade Games [agi-fanmade]
Mickey's Space Adventure [mickey]
Troll's Tale [troll]
Winnie the Pooh in the Hundred Acre Wood [winnie]
AGOS Games by Adventuresoft/Horrorsoft:
Elvira - Mistress of the Dark [elvira1]
@ -235,6 +239,13 @@ AGOS Games by Adventuresoft/Horrorsoft:
- Swampy Adventures [swampy]
The Feeble Files [feeble]
Composer Games by Animation Magic:
Darby the Dragon [darby]
Gregory and the Hot Air Balloon [gregory]
Magic Tales: Liam Finds a Story [liam]
The Princess and the Crab [princess]
Sleeping Cub's Test of Courage [sleepingcub]
GOB Games by Coktel Vision:
Bambou le sauveur de la jungle [bambou]
Bargon Attack [bargon]
@ -250,6 +261,22 @@ GOB Games by Coktel Vision:
Urban Runner [urban]
Ween: The Prophecy [ween]
Living Books Games:
Aesop's Fables: The Tortoise and the Hare [tortoise]
Arthur's Birthday [arthurbday]
Arthur's Teacher Trouble [arthur]
Dr. Seuss's ABC [seussabc]
Green Eggs and Ham [greeneggs]
Harry and the Haunted House [harryhh]
Just Grandma and Me [grandma]
Little Monster at School [lilmonster]
Ruff's Bone [ruff]
Sheila Rae, the Brave [sheila]
Stellaluna [stellaluna]
The Berenstain Bears Get in a Fight [bearfight]
The Berenstain Bears in the Dark [beardark]
The New Kid on the Block [newkid]
MADE Games by Activision:
Leather Goddesses of Phobos 2 [lgop2]
Return to Zork [rtz]
@ -257,34 +284,106 @@ MADE Games by Activision:
The Manhole [manhole]
Other Games:
3 Skulls of the Toltecs [toltecs]
Blue Force [blueforce]
Beneath a Steel Sky [sky]
Broken Sword: The Shadow of the Templars [sword1]
Broken Sword II: The Smoking Mirror [sword2]
Bud Tucker in Double Trouble [tucker]
Cruise for a Corpse [cruise]
Discworld [dw]
Discworld 2: Missing Presumed ...!? [dw2]
Dragon History [draci]
Drascula: The Vampire Strikes Back [drascula]
DreamWeb [dreamweb]
Eye of the Beholder [eob]
Eye of the Beholder II: The Legend of
Darkmoon [eob2]
Flight of the Amazon Queen [queen]
Future Wars [fw]
Hopkins FBI [hopkins]
Hugo's House of Horrors [hugo1]
Hugo 2: Whodunit? [hugo2]
Hugo 3: Jungle of Doom [hugo3]
I Have No Mouth, and I Must Scream [ihnm]
Inherit the Earth: Quest for the Orb [ite]
Nippon Safes Inc. [nippon]
Lands of Lore: The Throne of Chaos [lol]
Lure of the Temptress [lure]
Mortville Manor [mortevielle]
Nippon Safes Inc. [nippon]
Ringworld: Revenge Of The Patriarch [ringworld]
Return to Ringworld [ringworld2]
Sfinx [sfinx]
Soltys [soltys]
TeenAgent [teenagent]
The Journeyman Project: Pegasus Prime [pegasus]
The Legend of Kyrandia [kyra1]
The Legend of Kyrandia: The Hand of Fate [kyra2]
The Legend of Kyrandia: Malcolm's Revenge [kyra3]
The 7th Guest [t7g]
The Neverhood [neverhood]
Tony Tough and the Night of Roasted Moths [tony]
Toonstruck [toon]
Touche: The Adventures of the Fifth
Musketeer [touche]
Voyeur [voyeur]
SCI Games by Sierra Entertainment:
Castle of Dr. Brain [castlebrain]
Codename: ICEMAN [iceman]
Conquests of Camelot [camelot]
Conquests of the Longbow [longbow]
EcoQuest: The Search for Cetus [ecoquest]
EcoQuest 2: Lost Secret of the Rainforest [ecoquest2]
Freddy Pharkas: Frontier Pharmacist [freddypharkas]
Hoyle's Book of Games 1 [hoyle1]
Hoyle's Book of Games 2 [hoyle2]
Hoyle's Book of Games 3 [hoyle3]
Hoyle Classic Card Games [hoyle4]
Jones in the Fast Lane [jones]
King's Quest I [kq1sci]
King's Quest IV [kq4sci]
King's Quest V [kq5]
King's Quest VI [kq6]
Laura Bow: The Colonel's Bequest [laurabow]
Laura Bow 2: The Dagger of Amon Ra [laurabow2]
Leisure Suit Larry 1 [lsl1sci]
Leisure Suit Larry 2 [lsl2]
Leisure Suit Larry 3 [lsl3]
Leisure Suit Larry 5 [lsl5]
Leisure Suit Larry 6 [lsl6]
Mixed-up Fairy Tales [fairytales]
Mixed-up Mother Goose [mothergoose]
Pepper's Adventures in Time [pepper]
Police Quest 1 [pq1sci]
Police Quest 2 [pq2]
Police Quest 3 [pq3]
Quest for Glory 1/Hero's Quest [qfg1]
Quest for Glory 1 [qfg1vga]
Quest for Glory 2 [qfg2]
Quest for Glory 3 [qfg3]
Slater & Charlie Go Camping [slater]
Space Quest I [sq1sci]
Space Quest III [sq3]
Space Quest IV [sq4]
Space Quest V [sq5]
The Island of Dr. Brain [islandbrain]
Wintermute Games:
Chivalry is Not Dead [chivalry]
ZVISION Games by Activision:
Zork Nemesis: The Forbidden Lands [znemesis]
Zork: Grand Inquisitor [zgi]
SCUMM Games by Humongous Entertainment:
Backyard Baseball [baseball]
Backyard Baseball 2001 [baseball2001]
Backyard Baseball 2003 [baseball2003]
Backyard Football [football]
Backyard Football 2002 [football2002]
Bear Stormin' [brstorm]
Big Thinkers First Grade [thinker1]
Big Thinkers Kindergarten [thinkerk]
Blue's 123 Time Activities [Blues123Time]
@ -309,6 +408,7 @@ SCUMM Games by Humongous Entertainment:
Let's Explore the Airport with Buzzy [airport]
Let's Explore the Farm with Buzzy [farm]
Let's Explore the Jungle with Buzzy [jungle]
Pajama Sam: Games to Play on Any Day [pjgames]
Pajama Sam 1: No Need to Hide When It's
Dark Outside [pajama]
Pajama Sam 2: Thunder and Lightning
@ -333,38 +433,20 @@ SCUMM Games by Humongous Entertainment:
SPY Fox in Cheese Chase [chase]
SPY Fox in Hold the Mustard [mustard]
Living Books Games:
Aesop's Fables: The Tortoise and the Hare [tortoise]
Arthur's Birthday [arthurbday]
Arthur's Teacher Trouble [arthur]
Dr. Seuss's ABC [seussabc]
Green Eggs and Ham [greeneggs]
Harry and the Haunted House [harryhh]
Just Grandma and Me [grandma]
Little Monster at School [lilmonster]
Ruff's Bone [ruff]
Sheila Rae, the Brave [sheila]
Stellaluna [stellaluna]
The Berenstain Bears Get in a Fight [bearfight]
The Berenstain Bears in the Dark [beardark]
The New Kid on the Block [newkid]
The following games should load, but are not yet fully playable. Play
these at your own risk, and please do not file bug reports about them.
If you want the latest updates on game compatibility, visit our web site
and view the compatibility chart.
Backyard Football 2002 [football2002]
Backyard Soccer [soccer]
Backyard Soccer MLS [soccermls]
Backyard Soccer 2004 [soccer2004]
Blue's Treasure Hunt [BluesTreasureHunt]
Pajama Sam: Games to Play on Any Day [pjgames]
The following games are based on the SCUMM engine, but NOT supported
by ScummVM (yet):
Other Humongous Entertainment games
Moonbase Commander
Please be aware that the engines may contain bugs and unimplemented
features that sometimes make it impossible to finish the game. Save
@ -410,7 +492,29 @@ ScummVM will skip copy protection in the following games:
* Zak McKracken and the Alien Mindbenders
3.2) Commodore64 games notes:
3.2) Day of the Tentacle notes:
---- --------------------------
At one point in the game, you come across a computer that allows you
to play the original Maniac Mansion as an easter egg. ScummVM supports
this, with a few caveats:
ScummVM will scan your configuration file for a game that's in a
'Maniac' sub-folder of your Day of the Tentacle folder. If you've
copied the data files from the CD version, this should already be the
case but you have to add the game to ScummVM as well.
To return to Day of the Tentacle, press F5 and select "Return to
Launcher".
This means that you could in theory use any game as the easter egg.
Indeed, there is a "secret" configuration setting, "easter_egg", to
override the ID of the game to run. Be aware, though, that not all
games support returning to the launcher, and setting it up to use Day
of the Tentacle itself as the easter egg game is not recommended.
3.3) Commodore64 games notes:
---- ------------------------
Both Maniac Mansion and Zak McKracken run but Maniac Mansion is not yet
playable. Simply name the D64 disks "maniac1.d64" and "maniac2.d64"
@ -424,10 +528,10 @@ to Commodore64. We recommend using the much simpler approach described
in the previous paragraph.
3.3) Maniac Mansion NES notes:
3.4) Maniac Mansion NES notes:
---- -------------------------
Supported versions are English GB (E), French (F), German (G), Italian (I),
Swedish (SW) and English US (U). ScummVM requires just the PRG section
Swedish (SW) and English US (U). ScummVM requires just the PRG section
to run and not the whole ROM.
In order to get the game working, you will have to strip out the first
@ -453,7 +557,7 @@ section. To do so use the 'extract_mm_nes' utility from the tools
package.
3.4) Macintosh games notes:
3.5) Macintosh games notes:
---- ----------------------
All LucasArts SCUMM based adventures, except COMI, also exist in versions
for the Macintosh. ScummVM can use most (all?) of them, however, in some
@ -481,7 +585,7 @@ disk see:
http://wiki.scummvm.org/index.php/HOWTO-Mac_Games
3.5) Multi-CD games notes:
3.6) Multi-CD games notes:
---- ---------------------
In general, ScummVM does not deal very well with Multi-CD games. This is
because ScummVM assumes everything about a game can be found in one
@ -496,7 +600,7 @@ files. Usually, when a file appears on more than one CD you can pick
either of them.
3.6) The Curse of Monkey Island notes:
3.7) The Curse of Monkey Island notes:
---- ---------------------------------
For this game, you will need the comi.la0, comi.la1 and comi.la2 files.
The comi.la0 file can be found on either CD, but since they are
@ -508,7 +612,7 @@ two CDs. Some of the files appear on both CDs, but again they're
identical.
3.7) Broken Sword games notes:
3.8) Broken Sword games notes:
---- -------------------------
The instructions for the Broken Sword games are for the Sold-Out
Software versions, with each game on two CDs, since these were the
@ -517,7 +621,7 @@ them. Hopefully they are general enough to be useful to other releases
as well.
3.7.1) Broken Sword games cutscenes:
3.8.1) Broken Sword games cutscenes:
------ -----------------------------
The cutscenes for the Broken Sword games have a bit of a history (see
the next section, if you are interested), but in general all you need to
@ -558,7 +662,7 @@ currently does not work when running PlayStation videos. (Broken Sword
II already has subtitles; no extra work is needed for them.)
3.7.2) Broken Sword games cutscenes, in retrospect:
3.8.2) Broken Sword games cutscenes, in retrospect:
------ --------------------------------------------
The original releases of the Broken Sword games used RAD Game Tools's
Smacker(tm) format. As RAD was unwilling to open the older legacy
@ -583,7 +687,7 @@ decoding MPEG movies added a lot of complexity, and they didn't look as
good as the Smacker and DXA versions anyway.
3.7.3) Broken Sword:
3.8.3) Broken Sword:
------ -------------
For this game, you will need all of the files from the clusters
directories on both CDs. For the Windows and Macintosh versions, you
@ -600,7 +704,7 @@ makes little difference. The PlayStation version requires tunes.dat and
tunes.tab.
3.7.4) Broken Sword II:
3.8.4) Broken Sword II:
------ ----------------
For this game, you will need all of the files from the clusters
directories on both CDs. (Actually, a few of them may not be strictly
@ -615,7 +719,7 @@ In addition, you will need the cd.inf and, optionally, the startup.inf
files from the sword2 directory on CD 1.
3.8) Beneath a Steel Sky notes:
3.9) Beneath a Steel Sky notes:
---- --------------------------
Starting with ScummVM 0.8.0 you need the additional 'SKY.CPT' file to
run Beneath a Steel Sky.
@ -626,7 +730,7 @@ files (SKY.DNR, SKY.DSK), in your extrapath, or in the directory where
your ScummVM executable resides.
3.9) Flight of the Amazon Queen notes:
3.10) Flight of the Amazon Queen notes:
---- ---------------------------------
In order to use a non-freeware version of Flight of the Amazon Queen
(from original CD), you will need to place the 'queen.tbl' file
@ -641,7 +745,7 @@ specific version, and thus removing the run-time dependency on the
sound effects with MP3, OGG or FLAC.
3.10) Gobliiins notes:
3.11) Gobliiins notes:
----- ----------------
The CD versions of the Gobliiins series contain one big audio track
which you need to rip (see the section on using compressed audio files)
@ -651,7 +755,7 @@ track and its volume is therefore changed with the music volume control
as well.
3.11) Inherit the Earth: Quest for the Orb notes:
3.12) Inherit the Earth: Quest for the Orb notes:
----- -------------------------------------------
In order to run the Mac OS X Wyrmkeep re-release of the game you will
need to copy over data from the CD to your hard disk. If you're on a PC
@ -671,14 +775,14 @@ format, as they should include both resource and data forks. Copy all
'ITE *' files.
3.12) Simon the Sorcerer 1 and 2 notes:
3.13) Simon the Sorcerer 1 and 2 notes:
----- ---------------------------------
If you have the dual version of Simon the Sorcerer 1 or 2 on CD, you
will find the Windows version in the main directory of the CD and the
DOS version in the DOS directory of the CD.
3.13) The Feeble Files notes:
3.14) The Feeble Files notes:
----- -----------------------
If you have the Windows version of The Feeble Files, there are several
things to note.
@ -696,7 +800,7 @@ Rename voices.wav on CD3 to voices3.wav
Rename voices.wav on CD4 to voices4.wav
3.14) The Legend of Kyrandia notes:
3.15) The Legend of Kyrandia notes:
----- -----------------------------
To run The Legend of Kyrandia under ScummVM you need the 'kyra.dat'
file. The file should always be included in official ScummVM packages.
@ -707,7 +811,7 @@ thus you only need to grab it in case ScummVM complains about the file
being missing.
3.15) Sierra AGI games Predictive Input Dialog notes:
3.16) Sierra AGI games Predictive Input Dialog notes:
----- -----------------------------------------------
The Predictive Input Dialog is a ScummVM aid for running AGI engine
games (which notoriously require command line input) on devices with
@ -761,7 +865,7 @@ naturally mapping the functionality to the numeric keypad. Also, the
dialog's buttons can be navigated with the arrow and the enter keys.
3.16) Mickey's Space Adventure notes:
3.17) Mickey's Space Adventure notes:
----- -------------------------------
To run Mickey's Space Adventure under ScummVM, the original executable
of the game (mickey.exe) is needed together with the game's data files.
@ -776,7 +880,7 @@ game's screen to change location, similar to many adventure games, which
is simpler and more straightforward than moving around using the menu.
3.17) Winnie the Pooh notes:
3.18) Winnie the Pooh notes:
----- ----------------------
It is possible to import saved games from the original interpreter of the
game into ScummVM.
@ -791,14 +895,14 @@ game's screen to change location, similar to many adventure games, which
is simpler and more straightforward than moving around using the menu.
3.18) Troll's Tale notes:
3.19) Troll's Tale notes:
----- -------------------
The original game came in a PC booter disk, therefore it is necessary to
dump the contents of that disk in an image file and name it "troll.img"
to be able to play the game under ScummVM.
3.19) Dragon History notes:
3.20) Dragon History notes:
----- ---------------------
There are 4 language variants of the game: Czech, English, Polish and
German. Each of them is distributed in a separate archive. The only
@ -816,7 +920,7 @@ All game files and the walkthrough can be downloaded from
http://www.ucw.cz/draci-historie/index-en.html
3.20) Simultaneous speech and subtitles in Sierra SCI games:
3.21) Simultaneous speech and subtitles in Sierra SCI games:
----- ------------------------------------------------------
Certain CD versions of Sierra SCI games had both speech and text
resources. Some have an option to toggle between the two, but there are
@ -864,7 +968,7 @@ Space Quest 4 CD:
options dialog, or via ScummVM's audio options.
3.21) Known Problems:
3.22) Known Problems:
----- ---------------
This release has the following known problems. There is no need to
report them, although patches to fix them are welcome. If you discover a
@ -909,7 +1013,11 @@ site, please see the section on reporting bugs.
Inherit the Earth: Quest for the Orb
- Amiga versions aren't supported
Simon the Sorcerer 1:
Lure of the Temptress
- No Roland MT-32 support
- Sound support is incomplete and doesn't sound like original
Simon the Sorcerer 1:
- Subtitles aren't available in the English and German CD versions
as they are missing the majority of subtitles.
@ -1244,6 +1352,7 @@ Engines which currently support returning to the Launcher are:
TOUCHE
TSAGE
TUCKER
ZVISION
5.5) Hotkeys:
@ -1408,6 +1517,26 @@ other games.
t - Switch between 'Voice only',
'Voice and Text' and 'Text only'
Zork: Grand Inquisitor
Ctrl-s - Save
Ctrl-r - Restore
Ctrl-q - Quit
Ctrl-p - Preferences
F1 - Help
F5 - Inventory
F6 - Spellbook
F7 - Score
F8 - Put away current object/forget spell
F9 - Extract coin (must have the coin bag)
Space - Skips movies
Zork Nemesis: The Forbidden Lands
Ctrl-s - Save
Ctrl-r - Restore
Ctrl-q - Quit
Ctrl-p - Preferences
Space - Skips movies
Note that using Ctrl-f or Ctrl-g is not recommended: games can crash
when being run faster than their normal speed, as scripts will lose
synchronisation.
@ -1441,7 +1570,7 @@ The platforms that currently have a different default directory are:
<windir>\Profiles\username\Application Data\ScummVM\Saved games\
Saved games are stored under a hidden area in Windows NT4/2000/XP/Vista/7,
which can be accessed by running "%APPDATA%\ScummVM\Saved Games\" or by
which can be accessed by running "%APPDATA%\ScummVM\Saved Games\" or by
enabling hidden files in Windows Explorer.
Note for Windows NT4/2000/XP/Vista/7 users: The default saved games location
@ -1527,6 +1656,7 @@ Where 'xxx' is exact the saved game slot (ie 001) under ScummVM
TOUCHE
TSAGE
TUCKER
ZVISION
--save-slot/-x:
@ -1559,6 +1689,7 @@ Where 'xxx' is exact the saved game slot (ie 001) under ScummVM
TOUCHE
TSAGE
TUCKER
ZVISION
7.0) Music and Sound:
@ -2137,6 +2268,10 @@ Sierra games using the AGI engine add the following non-standard keywords:
originalsaveload bool If true, the original save/load screens are
used instead of the enhanced ScummVM ones
altamigapalette bool Use an alternative palette, common for all
Amiga games. This was the old behavior
mousesupport bool Enables mouse support. Allows to use mouse
for movement and in game menus
Sierra games using the SCI engine add the following non-standard keywords:
@ -2148,6 +2283,12 @@ Sierra games using the SCI engine add the following non-standard keywords:
native_fb01 bool If true, the music driver for an IBM Music
Feature card or a Yamaha FB-01 FM synth module
is used for MIDI output
use_cdaudio bool Use CD audio instead of in-game audio,
when available
windows_cursors bool Use the Windows cursors (smaller and monochrome)
instead of the DOS ones (King's Quest 6)
silver_cursors bool Use the alternate set of silver cursors,
instead of the normal golden ones (Space Quest 4)
Broken Sword II adds the following non-standard keywords:
@ -2240,6 +2381,27 @@ The 7th Guest adds the following non-standard keyword:
normal speed, to avoid music synchronization
issues
Zork Nemesis: The Forbidden Lands adds the following non-standard keywords:
originalsaveload bool If true, the original save/load screens are
used instead of the enhanced ScummVM ones
doublefps bool If true, game FPS are increased from 30 to 60
venusenabled bool If true, the in-game Venus help system is
enabled
noanimwhileturning bool If true, animations are disabled while turning
in panoramic mode
Zork: Grand Inquisitor adds the following non-standard keywords:
originalsaveload bool If true, the original save/load screens are
used instead of the enhanced ScummVM ones
doublefps bool If true, game FPS are increased from 30 to 60
noanimwhileturning bool If true, animations are disabled while turning
in panoramic mode
mpegmovies bool If true, the hires MPEG movies are used in the
DVD version of the game, instead of the lowres
AVI ones
8.2) Custom game options that can be toggled via the GUI
---- ---------------------------------------------------
@ -2308,9 +2470,11 @@ debug messages (see http://www.sysinternals.com/ntw2k/freeware/debugview.shtml).
* Type "./configure" in the ScummVM directory.
* You can now type 'make' to create a command line binary.
* To get a version you can run from Finder, type 'make bundle' which
will create ScummVM.app (this only works out of the box if you
installed SDL into /sw (as happens if you are using Fink). If you
have installed SDL in another way, you will have to edit ports.mk).
will create ScummVM.app (this tries to detect where the static libraries
are installed, which should work in most cases, but if it doesn't you
will need to specify this path with --with-staticlib-prefix= when calling
configure - for example "./configure --with-staticlib-prefix=/Users/foo"
if the libraries are in /Users/foo/lib).
* For more information refer to:
http://wiki.scummvm.org/index.php/Compiling_ScummVM/MacOS_X_Crosscompiling

View File

@ -246,6 +246,23 @@ void MP3Stream::initStream() {
_inStream->seek(0, SEEK_SET);
_curTime = mad_timer_zero;
_posInFrame = 0;
// Skip ID3 TAG if any
// ID3v1 (beginning with with 'TAG') is located at the end of files. So we can ignore those.
// ID3v2 can be located at the start of files and begins with a 10 bytes header, the first 3 bytes being 'ID3'.
// The tag size is coded on the last 4 bytes of the 10 bytes header as a 32 bit synchsafe integer.
// See http://id3.org/id3v2.4.0-structure for details.
char data[10];
_inStream->read(data, 10);
if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
uint32 size = data[9] + 128 * (data[8] + 128 * (data[7] + 128 * data[6]));
// This size does not include an optional 10 bytes footer. Check if it is present.
if (data[5] & 0x10)
size += 10;
debug("Skipping ID3 TAG (%d bytes)", size + 10);
_inStream->seek(size, SEEK_CUR);
} else
_inStream->seek(0, SEEK_SET);
// Update state
_state = MP3_STATE_READY;

View File

@ -241,6 +241,15 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &len
// If we have any samples that we need to skip (ie. we seeked into
// the middle of a chunk), skip them here.
if (_skipSamples != Timestamp()) {
if (_skipSamples > chunkLength) {
// If the amount we need to skip is greater than the size
// of the chunk, just skip it altogether.
_curMediaPos = _curMediaPos + chunkLength;
_skipSamples = _skipSamples - chunkLength;
delete stream;
continue;
}
skipSamples(_skipSamples, stream);
_curMediaPos = _curMediaPos + _skipSamples;
chunkLength = chunkLength - _skipSamples;

View File

@ -80,6 +80,12 @@ Config::DriverId Config::detect(OplType type) {
}
DriverId drv = parse(ConfMan.get("opl_driver"));
if (drv == kAuto) {
// Since the "auto" can be explicitly set for a game, and this
// driver shows up in the GUI as "<default>", check if there is
// a global setting for it before resorting to auto-detection.
drv = parse(ConfMan.get("opl_driver", Common::ConfigManager::kApplicationDomain));
}
// When a valid driver is selected, check whether it supports
// the requested OPL chip.

View File

@ -219,11 +219,10 @@ void ProtrackerStream::updateRow() {
case 0x0:
if (exy) {
_track[track].arpeggio = true;
if (note.period) {
_track[track].arpeggioNotes[0] = note.note;
_track[track].arpeggioNotes[1] = note.note + ex;
_track[track].arpeggioNotes[2] = note.note + ey;
}
byte trackNote = _module.periodToNote(_track[track].period);
_track[track].arpeggioNotes[0] = trackNote;
_track[track].arpeggioNotes[1] = trackNote + ex;
_track[track].arpeggioNotes[2] = trackNote + ey;
}
break;
case 0x1:

View File

@ -0,0 +1,348 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//#include <cstring>
#include "Analog.h"
namespace MT32Emu {
#if MT32EMU_USE_FLOAT_SAMPLES
/* FIR approximation of the overall impulse response of the cascade composed of the sample & hold circuit and the low pass filter
* of the MT-32 first generation.
* The coefficients below are found by windowing the inverse DFT of the 1024 pin frequency response converted to the minimum phase.
* The frequency response of the LPF is computed directly, the effect of the S&H is approximated by multiplying the LPF frequency
* response by the corresponding sinc. Although, the LPF has DC gain of 3.2, we ignore this in the emulation and use normalised model.
* The peak gain of the normalised cascade appears about 1.7 near 11.8 kHz. Relative error doesn't exceed 1% for the frequencies
* below 12.5 kHz. In the higher frequency range, the relative error is below 8%. Peak error value is at 16 kHz.
*/
static const float COARSE_LPF_TAPS_MT32[] = {
1.272473681f, -0.220267785f, -0.158039905f, 0.179603785f, -0.111484097f, 0.054137498f, -0.023518029f, 0.010997169f, -0.006935698f
};
// Similar approximation for new MT-32 and CM-32L/LAPC-I LPF. As the voltage controlled amplifier was introduced, LPF has unity DC gain.
// The peak gain value shifted towards higher frequencies and a bit higher about 1.83 near 13 kHz.
static const float COARSE_LPF_TAPS_CM32L[] = {
1.340615635f, -0.403331694f, 0.036005517f, 0.066156844f, -0.069672532f, 0.049563806f, -0.031113416f, 0.019169774f, -0.012421368f
};
#else
static const unsigned int COARSE_LPF_FRACTION_BITS = 14;
// Integer versions of the FIRs above multiplied by (1 << 14) and rounded.
static const SampleEx COARSE_LPF_TAPS_MT32[] = {
20848, -3609, -2589, 2943, -1827, 887, -385, 180, -114
};
static const SampleEx COARSE_LPF_TAPS_CM32L[] = {
21965, -6608, 590, 1084, -1142, 812, -510, 314, -204
};
#endif
/* Combined FIR that both approximates the impulse response of the analogue circuits of sample & hold and the low pass filter
* in the audible frequency range (below 20 kHz) and attenuates unwanted mirror spectra above 28 kHz as well. It is a polyphase
* filter intended for resampling the signal to 48 kHz yet for applying high frequency boost.
* As with the filter above, the analogue LPF frequency response is obtained for 1536 pin grid for range up to 96 kHz and multiplied
* by the corresponding sinc. The result is further squared, windowed and passed to generalised Parks-McClellan routine as a desired response.
* Finally, the minimum phase factor is found that's essentially the coefficients below.
* Relative error in the audible frequency range doesn't exceed 0.0006%, attenuation in the stopband is better than 100 dB.
* This level of performance makes it nearly bit-accurate for standard 16-bit sample resolution.
*/
// FIR version for MT-32 first generation.
static const float ACCURATE_LPF_TAPS_MT32[] = {
0.003429281f, 0.025929869f, 0.096587777f, 0.228884848f, 0.372413431f, 0.412386503f, 0.263980018f,
-0.014504962f, -0.237394528f, -0.257043496f, -0.103436603f, 0.063996095f, 0.124562333f, 0.083703206f,
0.013921662f, -0.033475018f, -0.046239712f, -0.029310921f, 0.00126585f, 0.021060961f, 0.017925605f,
0.003559874f, -0.005105248f, -0.005647917f, -0.004157918f, -0.002065664f, 0.00158747f, 0.003762585f,
0.001867137f, -0.001090028f, -0.001433979f, -0.00022367f, 4.34308E-05f, -0.000247827f, 0.000157087f,
0.000605823f, 0.000197317f, -0.000370511f, -0.000261202f, 9.96069E-05f, 9.85073E-05f, -5.28754E-05f,
-1.00912E-05f, 7.69943E-05f, 2.03162E-05f, -5.67967E-05f, -3.30637E-05f, 1.61958E-05f, 1.73041E-05f
};
// FIR version for new MT-32 and CM-32L/LAPC-I.
static const float ACCURATE_LPF_TAPS_CM32L[] = {
0.003917452f, 0.030693861f, 0.116424199f, 0.275101674f, 0.43217361f, 0.431247894f, 0.183255659f,
-0.174955671f, -0.354240244f, -0.212401714f, 0.072259178f, 0.204655344f, 0.108336211f, -0.039099027f,
-0.075138174f, -0.026261906f, 0.00582663f, 0.003052193f, 0.00613657f, 0.017017951f, 0.008732535f,
-0.011027427f, -0.012933664f, 0.001158097f, 0.006765958f, 0.00046778f, -0.002191106f, 0.001561017f,
0.001842871f, -0.001996876f, -0.002315836f, 0.000980965f, 0.001817454f, -0.000243272f, -0.000972848f,
0.000149941f, 0.000498886f, -0.000204436f, -0.000347415f, 0.000142386f, 0.000249137f, -4.32946E-05f,
-0.000131231f, 3.88575E-07f, 4.48813E-05f, -1.31906E-06f, -1.03499E-05f, 7.71971E-06f, 2.86721E-06f
};
// According to the CM-64 PCB schematic, there is a difference in the values of the LPF entrance resistors for the reverb and non-reverb channels.
// This effectively results in non-unity LPF DC gain for the reverb channel of 0.68 while the LPF has unity DC gain for the LA32 output channels.
// In emulation, the reverb output gain is multiplied by this factor to compensate for the LPF gain difference.
static const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
static const unsigned int OUTPUT_GAIN_FRACTION_BITS = 8;
static const float OUTPUT_GAIN_MULTIPLIER = float(1 << OUTPUT_GAIN_FRACTION_BITS);
static const unsigned int COARSE_LPF_DELAY_LINE_LENGTH = 8; // Must be a power of 2
static const unsigned int ACCURATE_LPF_DELAY_LINE_LENGTH = 16; // Must be a power of 2
static const unsigned int ACCURATE_LPF_NUMBER_OF_PHASES = 3; // Upsampling factor
static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_REGULAR = 2; // Downsampling factor
static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED = 1; // No downsampling
static const Bit32u ACCURATE_LPF_DELTAS_REGULAR[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 1, 0 }, { 1, 2, 1 } };
static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 0, 1 } };
class AbstractLowPassFilter {
public:
static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length);
virtual ~AbstractLowPassFilter() {}
virtual SampleEx process(SampleEx sample) = 0;
virtual bool hasNextSample() const;
virtual unsigned int getOutputSampleRate() const;
virtual unsigned int estimateInSampleCount(unsigned int outSamples) const;
virtual void addPositionIncrement(unsigned int) {}
};
class NullLowPassFilter : public AbstractLowPassFilter {
public:
SampleEx process(SampleEx sample);
};
class CoarseLowPassFilter : public AbstractLowPassFilter {
private:
const SampleEx * const LPF_TAPS;
SampleEx ringBuffer[COARSE_LPF_DELAY_LINE_LENGTH];
unsigned int ringBufferPosition;
public:
CoarseLowPassFilter(bool oldMT32AnalogLPF);
SampleEx process(SampleEx sample);
};
class AccurateLowPassFilter : public AbstractLowPassFilter {
private:
const float * const LPF_TAPS;
const Bit32u (* const deltas)[ACCURATE_LPF_NUMBER_OF_PHASES];
const unsigned int phaseIncrement;
const unsigned int outputSampleRate;
SampleEx ringBuffer[ACCURATE_LPF_DELAY_LINE_LENGTH];
unsigned int ringBufferPosition;
unsigned int phase;
public:
AccurateLowPassFilter(bool oldMT32AnalogLPF, bool oversample);
SampleEx process(SampleEx sample);
bool hasNextSample() const;
unsigned int getOutputSampleRate() const;
unsigned int estimateInSampleCount(unsigned int outSamples) const;
void addPositionIncrement(unsigned int positionIncrement);
};
Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) :
leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
synthGain(0),
reverbGain(0)
{}
Analog::~Analog() {
delete &leftChannelLPF;
delete &rightChannelLPF;
}
void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) {
if (outStream == NULL) {
leftChannelLPF.addPositionIncrement(outLength);
rightChannelLPF.addPositionIncrement(outLength);
return;
}
while (0 < (outLength--)) {
SampleEx outSampleL;
SampleEx outSampleR;
if (leftChannelLPF.hasNextSample()) {
outSampleL = leftChannelLPF.process(0);
outSampleR = rightChannelLPF.process(0);
} else {
SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain;
SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain;
#if !MT32EMU_USE_FLOAT_SAMPLES
inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
inSampleR >>= OUTPUT_GAIN_FRACTION_BITS;
#endif
outSampleL = leftChannelLPF.process(inSampleL);
outSampleR = rightChannelLPF.process(inSampleR);
}
*((*outStream)++) = Synth::clipSampleEx(outSampleL);
*((*outStream)++) = Synth::clipSampleEx(outSampleR);
}
}
unsigned int Analog::getOutputSampleRate() const {
return leftChannelLPF.getOutputSampleRate();
}
Bit32u Analog::getDACStreamsLength(Bit32u outputLength) const {
return leftChannelLPF.estimateInSampleCount(outputLength);
}
void Analog::setSynthOutputGain(float useSynthGain) {
#if MT32EMU_USE_FLOAT_SAMPLES
synthGain = useSynthGain;
#else
if (OUTPUT_GAIN_MULTIPLIER < useSynthGain) useSynthGain = OUTPUT_GAIN_MULTIPLIER;
synthGain = SampleEx(useSynthGain * OUTPUT_GAIN_MULTIPLIER);
#endif
}
void Analog::setReverbOutputGain(float useReverbGain, bool mt32ReverbCompatibilityMode) {
if (!mt32ReverbCompatibilityMode) useReverbGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
#if MT32EMU_USE_FLOAT_SAMPLES
reverbGain = useReverbGain;
#else
if (OUTPUT_GAIN_MULTIPLIER < useReverbGain) useReverbGain = OUTPUT_GAIN_MULTIPLIER;
reverbGain = SampleEx(useReverbGain * OUTPUT_GAIN_MULTIPLIER);
#endif
}
AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
switch (mode) {
case AnalogOutputMode_COARSE:
return *new CoarseLowPassFilter(oldMT32AnalogLPF);
case AnalogOutputMode_ACCURATE:
return *new AccurateLowPassFilter(oldMT32AnalogLPF, false);
case AnalogOutputMode_OVERSAMPLED:
return *new AccurateLowPassFilter(oldMT32AnalogLPF, true);
default:
return *new NullLowPassFilter;
}
}
void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) {
#if MT32EMU_USE_FLOAT_SAMPLES
SampleEx *p = ringBuffer;
while (length--) {
*(p++) = 0.0f;
}
#else
memset(ringBuffer, 0, length * sizeof(SampleEx));
#endif
}
bool AbstractLowPassFilter::hasNextSample() const {
return false;
}
unsigned int AbstractLowPassFilter::getOutputSampleRate() const {
return SAMPLE_RATE;
}
unsigned int AbstractLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
return outSamples;
}
SampleEx NullLowPassFilter::process(const SampleEx inSample) {
return inSample;
}
CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
ringBufferPosition(0)
{
muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
}
SampleEx CoarseLowPassFilter::process(const SampleEx inSample) {
static const unsigned int DELAY_LINE_MASK = COARSE_LPF_DELAY_LINE_LENGTH - 1;
SampleEx sample = LPF_TAPS[COARSE_LPF_DELAY_LINE_LENGTH] * ringBuffer[ringBufferPosition];
ringBuffer[ringBufferPosition] = Synth::clipSampleEx(inSample);
for (unsigned int i = 0; i < COARSE_LPF_DELAY_LINE_LENGTH; i++) {
sample += LPF_TAPS[i] * ringBuffer[(i + ringBufferPosition) & DELAY_LINE_MASK];
}
ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
#if !MT32EMU_USE_FLOAT_SAMPLES
sample >>= COARSE_LPF_FRACTION_BITS;
#endif
return sample;
}
AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const bool oversample) :
LPF_TAPS(oldMT32AnalogLPF ? ACCURATE_LPF_TAPS_MT32 : ACCURATE_LPF_TAPS_CM32L),
deltas(oversample ? ACCURATE_LPF_DELTAS_OVERSAMPLED : ACCURATE_LPF_DELTAS_REGULAR),
phaseIncrement(oversample ? ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED : ACCURATE_LPF_PHASE_INCREMENT_REGULAR),
outputSampleRate(SAMPLE_RATE * ACCURATE_LPF_NUMBER_OF_PHASES / phaseIncrement),
ringBufferPosition(0),
phase(0)
{
muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
}
SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
static const unsigned int DELAY_LINE_MASK = ACCURATE_LPF_DELAY_LINE_LENGTH - 1;
float sample = (phase == 0) ? LPF_TAPS[ACCURATE_LPF_DELAY_LINE_LENGTH * ACCURATE_LPF_NUMBER_OF_PHASES] * ringBuffer[ringBufferPosition] : 0.0f;
if (!hasNextSample()) {
ringBuffer[ringBufferPosition] = inSample;
}
for (unsigned int tapIx = phase, delaySampleIx = 0; delaySampleIx < ACCURATE_LPF_DELAY_LINE_LENGTH; delaySampleIx++, tapIx += ACCURATE_LPF_NUMBER_OF_PHASES) {
sample += LPF_TAPS[tapIx] * ringBuffer[(delaySampleIx + ringBufferPosition) & DELAY_LINE_MASK];
}
phase += phaseIncrement;
if (ACCURATE_LPF_NUMBER_OF_PHASES <= phase) {
phase -= ACCURATE_LPF_NUMBER_OF_PHASES;
ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
}
return SampleEx(ACCURATE_LPF_NUMBER_OF_PHASES * sample);
}
bool AccurateLowPassFilter::hasNextSample() const {
return phaseIncrement <= phase;
}
unsigned int AccurateLowPassFilter::getOutputSampleRate() const {
return outputSampleRate;
}
unsigned int AccurateLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
Bit32u cycleCount = outSamples / ACCURATE_LPF_NUMBER_OF_PHASES;
Bit32u remainder = outSamples - cycleCount * ACCURATE_LPF_NUMBER_OF_PHASES;
return cycleCount * phaseIncrement + deltas[remainder][phase];
}
void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncrement) {
phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES;
}
}

View File

@ -0,0 +1,57 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_ANALOG_H
#define MT32EMU_ANALOG_H
#include "mt32emu.h"
namespace MT32Emu {
class AbstractLowPassFilter;
/* Analog class is dedicated to perform fair emulation of analogue circuitry of hardware units that is responsible
* for processing output signal after the DAC. It appears that the analogue circuit labeled "LPF" on the schematic
* also applies audible changes to the signal spectra. There is a significant boost of higher frequencies observed
* aside from quite poor attenuation of the mirror spectra above 16 kHz which is due to a relatively low filter order.
*
* As the final mixing of multiplexed output signal is performed after the DAC, this function is migrated here from Synth.
* Saying precisely, mixing is performed within the LPF as the entrance resistors are actually components of a LPF
* designed using the multiple feedback topology. Nevertheless, the schematic separates them.
*/
class Analog {
public:
Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures);
~Analog();
void process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, const Bit32u outLength);
unsigned int getOutputSampleRate() const;
Bit32u getDACStreamsLength(Bit32u outputLength) const;
void setSynthOutputGain(float synthGain);
void setReverbOutputGain(float reverbGain, bool mt32ReverbCompatibilityMode);
private:
AbstractLowPassFilter &leftChannelLPF;
AbstractLowPassFilter &rightChannelLPF;
SampleEx synthGain;
SampleEx reverbGain;
Analog(Analog &);
};
}
#endif

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//#include <memory.h>
//#include <cstring>
#include "mt32emu.h"
#include "BReverbModel.h"
@ -501,9 +501,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
* Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below.
* So, despite this isn't actually accurate, we only add the check here for performance reasons.
*/
Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1)) + (Bit32s)outL2) + Bit32s(outL2 >> 1)) + (Bit32s)outL3);
Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1)) + (SampleEx)outL2) + SampleEx(outL2 >> 1)) + (SampleEx)outL3);
#else
Sample outSample = Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1) + (Bit32s)outL2 + Bit32s(outL2 >> 1) + (Bit32s)outL3);
Sample outSample = Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1) + (SampleEx)outL2 + SampleEx(outL2 >> 1) + (SampleEx)outL3);
#endif
*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF);
}
@ -515,9 +515,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
Sample outSample = 1.5f * (outR1 + outR2) + outR3;
#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
// See the note above for the left channel output.
Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1)) + (Bit32s)outR2) + Bit32s(outR2 >> 1)) + (Bit32s)outR3);
Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1)) + (SampleEx)outR2) + SampleEx(outR2 >> 1)) + (SampleEx)outR3);
#else
Sample outSample = Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1) + (Bit32s)outR2 + Bit32s(outR2 >> 1) + (Bit32s)outR3);
Sample outSample = Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1) + (SampleEx)outR2 + SampleEx(outR2 >> 1) + (SampleEx)outR3);
#endif
*(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
}

View File

@ -95,7 +95,6 @@ class BReverbModel {
const bool tapDelayMode;
Bit32u dryAmp;
Bit32u wetLevel;
void mute();
static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode);
static const BReverbSettings &getMT32Settings(const ReverbMode mode);
@ -107,6 +106,7 @@ public:
void open();
// May be called multiple times without an open() in between.
void close();
void mute();
void setParameters(Bit8u time, Bit8u level);
void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples);
bool isActive() const;

View File

@ -18,7 +18,7 @@
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "LA32FloatWaveGenerator.h"
#include "internals.h"
namespace MT32Emu {

View File

@ -50,8 +50,8 @@ We haven't fully explored:
//#include <cmath>
#include "mt32emu.h"
#include "LA32Ramp.h"
#include "mmath.h"
#include "internals.h"
namespace MT32Emu {

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "LA32WaveGenerator.h"
#if MT32EMU_USE_FLOAT_SAMPLES
#include "LA32FloatWaveGenerator.cpp"
#else
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h"
namespace MT32Emu {
static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18;

View File

@ -0,0 +1,124 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_MEMORY_REGION_H
#define MT32EMU_MEMORY_REGION_H
namespace MT32Emu {
enum MemoryRegionType {
MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
};
class MemoryRegion {
private:
Synth *synth;
Bit8u *realMemory;
Bit8u *maxTable;
public:
MemoryRegionType type;
Bit32u startAddr, entrySize, entries;
MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
synth = useSynth;
realMemory = useRealMemory;
maxTable = useMaxTable;
type = useType;
startAddr = useStartAddr;
entrySize = useEntrySize;
entries = useEntries;
}
int lastTouched(Bit32u addr, Bit32u len) const {
return (offset(addr) + len - 1) / entrySize;
}
int firstTouchedOffset(Bit32u addr) const {
return offset(addr) % entrySize;
}
int firstTouched(Bit32u addr) const {
return offset(addr) / entrySize;
}
Bit32u regionEnd() const {
return startAddr + entrySize * entries;
}
bool contains(Bit32u addr) const {
return addr >= startAddr && addr < regionEnd();
}
int offset(Bit32u addr) const {
return addr - startAddr;
}
Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
if (addr + len > regionEnd())
return regionEnd() - addr;
return len;
}
Bit32u next(Bit32u addr, Bit32u len) const {
if (addr + len > regionEnd()) {
return regionEnd() - addr;
}
return 0;
}
Bit8u getMaxValue(int off) const {
if (maxTable == NULL)
return 0xFF;
return maxTable[off % entrySize];
}
Bit8u *getRealMemory() const {
return realMemory;
}
bool isReadable() const {
return getRealMemory() != NULL;
}
void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
};
class PatchTempMemoryRegion : public MemoryRegion {
public:
PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
};
class RhythmTempMemoryRegion : public MemoryRegion {
public:
RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
};
class TimbreTempMemoryRegion : public MemoryRegion {
public:
TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
};
class PatchesMemoryRegion : public MemoryRegion {
public:
PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
};
class TimbresMemoryRegion : public MemoryRegion {
public:
TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
};
class SystemMemoryRegion : public MemoryRegion {
public:
SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
};
class DisplayMemoryRegion : public MemoryRegion {
public:
DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
};
class ResetMemoryRegion : public MemoryRegion {
public:
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
};
}
#endif

View File

@ -0,0 +1,67 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_MIDI_EVENT_QUEUE_H
#define MT32EMU_MIDI_EVENT_QUEUE_H
namespace MT32Emu {
/**
* Used to safely store timestamped MIDI events in a local queue.
*/
struct MidiEvent {
Bit32u shortMessageData;
const Bit8u *sysexData;
Bit32u sysexLength;
Bit32u timestamp;
~MidiEvent();
void setShortMessage(Bit32u shortMessageData, Bit32u timestamp);
void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
};
/**
* Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it.
* It is intended to:
* - get rid of prerenderer while retaining graceful partial abortion
* - add fair emulation of the MIDI interface delays
* - extend the synth interface with the default implementation of a typical rendering loop.
* THREAD SAFETY:
* It is safe to use either in a single thread environment or when there are only two threads - one performs only reading
* and one performs only writing. More complicated usage requires external synchronisation.
*/
class MidiEventQueue {
private:
MidiEvent * const ringBuffer;
const Bit32u ringBufferMask;
volatile Bit32u startPosition;
volatile Bit32u endPosition;
public:
MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE); // Must be a power of 2
~MidiEventQueue();
void reset();
bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
const MidiEvent *peekMidiEvent();
void dropMidiEvent();
bool isFull() const;
};
}
#endif

View File

@ -19,6 +19,7 @@
//#include <cstring>
#include "mt32emu.h"
#include "internals.h"
#include "PartialManager.h"
namespace MT32Emu {

View File

@ -21,6 +21,7 @@
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h"
namespace MT32Emu {
@ -312,8 +313,8 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len
// Though, it is unknown whether this overflow is exploited somewhere.
Sample leftOut = Sample((sample * leftPanValue) >> 8);
Sample rightOut = Sample((sample * rightPanValue) >> 8);
*leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + (Bit32s)leftOut);
*rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + (Bit32s)rightOut);
*leftBuf = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut);
*rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut);
leftBuf++;
rightBuf++;
#endif

View File

@ -18,6 +18,7 @@
//#include <cstring>
#include "mt32emu.h"
#include "internals.h"
#include "PartialManager.h"
namespace MT32Emu {

View File

@ -16,6 +16,7 @@
*/
#include "mt32emu.h"
#include "internals.h"
namespace MT32Emu {

View File

@ -21,6 +21,7 @@
namespace MT32Emu {
class Part;
class Partial;
enum PolyState {
POLY_Playing,

View File

@ -21,8 +21,8 @@
namespace MT32Emu {
static const ROMInfo *getKnownROMInfoFromList(unsigned int index) {
static const ControlROMFeatureSet MT32_COMPATIBLE(true);
static const ControlROMFeatureSet CM32L_COMPATIBLE(false);
static const ControlROMFeatureSet MT32_COMPATIBLE(true, true);
static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false);
// Known ROMs
static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
@ -106,7 +106,6 @@ void ROMImage::freeROMImage(const ROMImage *romImage) {
delete romImage;
}
Common::File* ROMImage::getFile() const {
return file;
}
@ -115,11 +114,17 @@ const ROMInfo* ROMImage::getROMInfo() const {
return romInfo;
}
ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible) : defaultReverbMT32Compatible(useDefaultReverbMT32Compatible) {
}
ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) :
defaultReverbMT32Compatible(useDefaultReverbMT32Compatible),
oldMT32AnalogLPF(useOldMT32AnalogLPF)
{}
bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const {
return defaultReverbMT32Compatible;
}
bool ControlROMFeatureSet::isOldMT32AnalogLPF() const {
return oldMT32AnalogLPF;
}
}

View File

@ -77,10 +77,12 @@ public:
struct ControlROMFeatureSet {
private:
unsigned int defaultReverbMT32Compatible : 1;
unsigned int oldMT32AnalogLPF : 1;
public:
ControlROMFeatureSet(bool defaultReverbMT32Compatible);
ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF);
bool isDefaultReverbMT32Compatible() const;
bool isOldMT32AnalogLPF() const;
};
}

View File

@ -31,19 +31,6 @@ namespace MT32Emu {
#define MT32EMU_ALIGN_PACKED __attribute__((packed))
#endif
typedef unsigned int Bit32u;
typedef signed int Bit32s;
typedef unsigned short int Bit16u;
typedef signed short int Bit16s;
typedef unsigned char Bit8u;
typedef signed char Bit8s;
#if MT32EMU_USE_FLOAT_SAMPLES
typedef float Sample;
#else
typedef Bit16s Sample;
#endif
// The following structures represent the MT-32's memory
// Since sysex allows this memory to be written to in blocks of bytes,
// we keep this packed so that we can copy data into the various
@ -184,7 +171,37 @@ struct MemParams {
#pragma pack()
#endif
struct ControlROMPCMStruct;
struct ControlROMMap {
Bit16u idPos;
Bit16u idLen;
const char *idBytes;
Bit16u pcmTable; // 4 * pcmCount bytes
Bit16u pcmCount;
Bit16u timbreAMap; // 128 bytes
Bit16u timbreAOffset;
bool timbreACompressed;
Bit16u timbreBMap; // 128 bytes
Bit16u timbreBOffset;
bool timbreBCompressed;
Bit16u timbreRMap; // 2 * timbreRCount bytes
Bit16u timbreRCount;
Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
Bit16u rhythmSettingsCount;
Bit16u reserveSettings; // 9 bytes
Bit16u panSettings; // 8 bytes
Bit16u programSettings; // 8 bytes
Bit16u rhythmMaxTable; // 4 bytes
Bit16u patchMaxTable; // 16 bytes
Bit16u systemMaxTable; // 23 bytes
Bit16u timbreMaxTable; // 72 bytes
};
struct ControlROMPCMStruct {
Bit8u pos;
Bit8u len;
Bit8u pitchLSB;
Bit8u pitchMSB;
};
struct PCMWaveEntry {
Bit32u addr;
@ -216,8 +233,6 @@ struct PatchCache {
const TimbreParam::PartialParam *partialParam;
};
class Partial; // Forward reference for class defined in partial.h
}
#endif

View File

@ -22,12 +22,19 @@
#include "mt32emu.h"
#include "mmath.h"
#include "PartialManager.h"
#include "internals.h"
#include "Analog.h"
#include "BReverbModel.h"
#include "common/debug.h"
#include "MemoryRegion.h"
#include "MidiEventQueue.h"
#include "PartialManager.h"
namespace MT32Emu {
// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
static const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
static const ControlROMMap ControlROMMaps[7] = {
// ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax
{0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
@ -46,18 +53,15 @@ static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
}
}
Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) {
unsigned int checksum = -initChecksum;
for (unsigned int i = 0; i < len; i++) {
checksum = checksum + data[i];
checksum -= data[i];
}
checksum = checksum & 0x7f;
if (checksum) {
checksum = 0x80 - checksum;
}
return checksum;
return Bit8u(checksum & 0x7f);
}
Synth::Synth(ReportHandler *useReportHandler) {
Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) {
isOpen = false;
reverbOverridden = false;
partialCount = DEFAULT_MAX_PARTIALS;
@ -75,6 +79,7 @@ Synth::Synth(ReportHandler *useReportHandler) {
reverbModels[i] = NULL;
}
reverbModel = NULL;
analog = NULL;
setDACInputMode(DACInputMode_NICE);
setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
setOutputGain(1.0f);
@ -92,6 +97,8 @@ Synth::~Synth() {
if (isDefaultReportHandler) {
delete reportHandler;
}
delete &mt32ram;
delete &mt32default;
}
void ReportHandler::showLCDMessage(const char *data) {
@ -126,7 +133,7 @@ void Synth::printDebug(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
#if MT32EMU_DEBUG_SAMPLESTAMPS > 0
reportHandler->printDebug("[%u] ", renderedSampleCount);
reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount);
#endif
reportHandler->printDebug(fmt, ap);
va_end(ap);
@ -211,10 +218,7 @@ MIDIDelayMode Synth::getMIDIDelayMode() const {
void Synth::setOutputGain(float newOutputGain) {
if (newOutputGain < 0.0f) newOutputGain = -newOutputGain;
outputGain = newOutputGain;
#if !MT32EMU_USE_FLOAT_SAMPLES
if (256.0f < newOutputGain) newOutputGain = 256.0f;
effectiveOutputGain = int(newOutputGain * 256.0f);
#endif
if (analog != NULL) analog->setSynthOutputGain(newOutputGain);
}
float Synth::getOutputGain() const {
@ -224,13 +228,7 @@ float Synth::getOutputGain() const {
void Synth::setReverbOutputGain(float newReverbOutputGain) {
if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain;
reverbOutputGain = newReverbOutputGain;
if (!isMT32ReverbCompatibilityMode()) newReverbOutputGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
#if MT32EMU_USE_FLOAT_SAMPLES
effectiveReverbOutputGain = newReverbOutputGain;
#else
if (256.0f < newReverbOutputGain) newReverbOutputGain = 256.0f;
effectiveReverbOutputGain = int(newReverbOutputGain * 256.0f);
#endif
if (analog != NULL) analog->setReverbOutputGain(newReverbOutputGain, isMT32ReverbCompatibilityMode());
}
float Synth::getReverbOutputGain() const {
@ -393,7 +391,11 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
return true;
}
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) {
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) {
return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
}
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
if (isOpen) {
return false;
}
@ -548,6 +550,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
midiQueue = new MidiEventQueue();
analog = new Analog(analogOutputMode, controlROMFeatures);
setOutputGain(outputGain);
setReverbOutputGain(reverbOutputGain);
isOpen = true;
isEnabled = false;
@ -565,6 +571,9 @@ void Synth::close(bool forced) {
delete midiQueue;
midiQueue = NULL;
delete analog;
analog = NULL;
delete partialManager;
partialManager = NULL;
@ -603,16 +612,37 @@ void Synth::flushMIDIQueue() {
}
}
void Synth::setMIDIEventQueueSize(Bit32u useSize) {
if (midiQueue != NULL) {
flushMIDIQueue();
delete midiQueue;
midiQueue = new MidiEventQueue(useSize);
Bit32u Synth::setMIDIEventQueueSize(Bit32u useSize) {
static const Bit32u MAX_QUEUE_SIZE = (1 << 24); // This results in about 256 Mb - much greater than any reasonable value
if (midiQueue == NULL) return 0;
flushMIDIQueue();
// Find a power of 2 that is >= useSize
Bit32u binarySize = 1;
if (useSize < MAX_QUEUE_SIZE) {
// Using simple linear search as this isn't time critical
while (binarySize < useSize) binarySize <<= 1;
} else {
binarySize = MAX_QUEUE_SIZE;
}
delete midiQueue;
midiQueue = new MidiEventQueue(binarySize);
return binarySize;
}
Bit32u Synth::getShortMessageLength(Bit32u msg) {
if ((msg & 0xF0) == 0xF0) return 1;
if ((msg & 0xF0) == 0xF0) {
switch (msg & 0xFF) {
case 0xF1:
case 0xF3:
return 2;
case 0xF2:
return 3;
default:
return 1;
}
}
// NOTE: This calculation isn't quite correct
// as it doesn't consider the running status byte
return ((msg & 0xE0) == 0xC0) ? 2 : 3;
@ -638,6 +668,7 @@ bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
}
if (!isEnabled) isEnabled = true;
return midiQueue->pushShortMessage(msg, timestamp);
}
@ -650,16 +681,19 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
timestamp = addMIDIInterfaceDelay(len, timestamp);
}
if (!isEnabled) isEnabled = true;
return midiQueue->pushSysex(sysex, len, timestamp);
}
void Synth::playMsgNow(Bit32u msg) {
// FIXME: Implement active sensing
// NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope.
// It is assumed that realtime consumers of the library respond to these MIDI events as appropriate.
unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
unsigned char chan = (unsigned char)(msg & 0x00000F);
unsigned char note = (unsigned char)((msg & 0x007F00) >> 8);
unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
isEnabled = true;
if (!isEnabled) isEnabled = true;
//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
@ -831,7 +865,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
return;
}
unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
Bit8u checksum = calcSysexChecksum(sysex, len - 1);
if (checksum != sysex[len - 1]) {
printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
return;
@ -1410,9 +1444,8 @@ void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32
memcpy(dstSysexData, useSysexData, sysexLength);
}
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) {
ringBuffer = new MidiEvent[ringBufferSize];
memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent));
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1) {
memset(ringBuffer, 0, useRingBufferSize * sizeof(MidiEvent));
reset();
}
@ -1426,7 +1459,7 @@ void MidiEventQueue::reset() {
}
bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
// Is ring buffer full?
if (startPosition == newEndPosition) return false;
ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
@ -1435,7 +1468,7 @@ bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp)
}
bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
// Is ring buffer full?
if (startPosition == newEndPosition) return false;
ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
@ -1450,31 +1483,36 @@ const MidiEvent *MidiEventQueue::peekMidiEvent() {
void MidiEventQueue::dropMidiEvent() {
// Is ring buffer empty?
if (startPosition != endPosition) {
startPosition = (startPosition + 1) % ringBufferSize;
startPosition = (startPosition + 1) & ringBufferMask;
}
}
bool MidiEventQueue::isFull() const {
return startPosition == ((endPosition + 1) & ringBufferMask);
}
unsigned int Synth::getStereoOutputSampleRate() const {
return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
}
void Synth::render(Sample *stream, Bit32u len) {
Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
Sample tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
Sample tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
Sample tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
if (!isEnabled) {
renderedSampleCount += analog->getDACStreamsLength(len);
analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
muteSampleBuffer(stream, len << 1);
return;
}
// As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
while (len > 0) {
Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen);
for (Bit32u i = 0; i < thisLen; i++) {
#if MT32EMU_USE_FLOAT_SAMPLES
*(stream++) = tmpNonReverbLeft[i] + tmpReverbDryLeft[i] + tmpReverbWetLeft[i];
*(stream++) = tmpNonReverbRight[i] + tmpReverbDryRight[i] + tmpReverbWetRight[i];
#else
*(stream++) = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
*(stream++) = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
#endif
}
len -= thisLen;
Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen));
analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
len -= thisPassLen;
}
}
@ -1518,7 +1556,10 @@ void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample
// In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted.
// In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy.
void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
#if !MT32EMU_USE_FLOAT_SAMPLES
#if MT32EMU_USE_FLOAT_SAMPLES
(void)buffer;
(void)len;
#else
switch (dacInputMode) {
case DACInputMode_GENERATION2:
while (len--) {
@ -1528,7 +1569,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
break;
case DACInputMode_NICE:
while (len--) {
*buffer = clipBit16s(Bit32s(*buffer) << 1);
*buffer = clipSampleEx(SampleEx(*buffer) << 1);
++buffer;
}
break;
@ -1538,26 +1579,16 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
#endif
}
void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb) {
if (dacInputMode == DACInputMode_PURE) return;
void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
float gain = reverb ? effectiveReverbOutputGain : outputGain;
while (len--) {
*(buffer++) *= gain;
}
(void)buffer;
(void)len;
#else
int gain = reverb ? effectiveReverbOutputGain : effectiveOutputGain;
if (dacInputMode == DACInputMode_GENERATION1) {
while (len--) {
Bit32s target = Bit16s((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
*(buffer++) = clipBit16s((target * gain) >> 8);
*buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
++buffer;
}
return;
}
while (len--) {
*buffer = clipBit16s((Bit32s(*buffer) * gain) >> 8);
++buffer;
}
#endif
}
@ -1566,18 +1597,18 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
// Even if LA32 output isn't desired, we proceed anyway with temp buffers
Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN];
if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft;
if (nonReverbLeft == NULL) nonReverbRight = tmpBufNonReverbRight;
if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight;
Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN];
if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft;
if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight;
muteSampleBuffer(nonReverbLeft, len);
muteSampleBuffer(nonReverbRight, len);
muteSampleBuffer(reverbDryLeft, len);
muteSampleBuffer(reverbDryRight, len);
if (isEnabled) {
muteSampleBuffer(nonReverbLeft, len);
muteSampleBuffer(nonReverbRight, len);
muteSampleBuffer(reverbDryLeft, len);
muteSampleBuffer(reverbDryRight, len);
for (unsigned int i = 0; i < getPartialCount(); i++) {
if (partialManager->shouldReverb(i)) {
partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
@ -1591,8 +1622,8 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
if (isReverbEnabled()) {
reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len, true);
if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len, true);
if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
} else {
muteSampleBuffer(reverbWetLeft, len);
muteSampleBuffer(reverbWetRight, len);
@ -1601,15 +1632,20 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
// Don't bother with conversion if the output is going to be unused
if (nonReverbLeft != tmpBufNonReverbLeft) {
produceLA32Output(nonReverbLeft, len);
convertSamplesToOutput(nonReverbLeft, len, false);
convertSamplesToOutput(nonReverbLeft, len);
}
if (nonReverbRight != tmpBufNonReverbRight) {
produceLA32Output(nonReverbRight, len);
convertSamplesToOutput(nonReverbRight, len, false);
convertSamplesToOutput(nonReverbRight, len);
}
if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len, false);
if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len, false);
if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
} else {
// Avoid muting buffers that wasn't requested
if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len);
if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len);
if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len);
if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len);
muteSampleBuffer(reverbWetLeft, len);
muteSampleBuffer(reverbWetRight, len);
}
@ -1651,14 +1687,48 @@ bool Synth::isActive() const {
return false;
}
const Partial *Synth::getPartial(unsigned int partialNum) const {
return partialManager->getPartial(partialNum);
}
unsigned int Synth::getPartialCount() const {
return partialCount;
}
void Synth::getPartStates(bool *partStates) const {
for (int partNumber = 0; partNumber < 9; partNumber++) {
const Part *part = parts[partNumber];
partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0;
}
}
void Synth::getPartialStates(PartialState *partialStates) const {
static const PartialState partialPhaseToState[8] = {
PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
};
for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
const Partial *partial = partialManager->getPartial(partialNum);
partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
}
}
unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const {
unsigned int playingNotes = 0;
if (isOpen && (partNumber < 9)) {
const Part *part = parts[partNumber];
const Poly *poly = part->getFirstActivePoly();
while (poly != NULL) {
keys[playingNotes] = (Bit8u)poly->getKey();
velocities[playingNotes] = (Bit8u)poly->getVelocity();
playingNotes++;
poly = poly->getNext();
}
}
return playingNotes;
}
const char *Synth::getPatchName(unsigned int partNumber) const {
return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
}
const Part *Synth::getPart(unsigned int partNum) const {
if (partNum > 8) {
return NULL;

View File

@ -19,15 +19,31 @@
#define MT32EMU_SYNTH_H
//#include <cstdarg>
//#include <cstring>
namespace MT32Emu {
class TableInitialiser;
class Analog;
class BReverbModel;
class MemoryRegion;
class MidiEventQueue;
class Part;
class Poly;
class Partial;
class PartialManager;
class Part;
class ROMImage;
class BReverbModel;
class PatchTempMemoryRegion;
class RhythmTempMemoryRegion;
class TimbreTempMemoryRegion;
class PatchesMemoryRegion;
class TimbresMemoryRegion;
class SystemMemoryRegion;
class DisplayMemoryRegion;
class ResetMemoryRegion;
struct ControlROMMap;
struct PCMWaveEntry;
struct MemParams;
/**
* Methods for emulating the connection between the LA32 and the DAC, which involves
@ -43,8 +59,7 @@ enum DACInputMode {
// Produces samples that exactly match the bits output from the emulated LA32.
// * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
// * Much less likely to overdrive than any other mode.
// * Half the volume of any of the other modes, meaning its volume relative to the reverb
// output when mixed together directly will sound wrong.
// * Half the volume of any of the other modes.
// * Output gain is ignored for both LA32 and reverb output.
// * Perfect for developers while debugging :)
DACInputMode_PURE,
@ -60,6 +75,7 @@ enum DACInputMode {
DACInputMode_GENERATION2
};
// Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface.
enum MIDIDelayMode {
// Process incoming MIDI events immediately.
MIDIDelayMode_IMMEDIATE,
@ -72,6 +88,35 @@ enum MIDIDelayMode {
MIDIDelayMode_DELAY_ALL
};
// Methods for emulating the effects of analogue circuits of real hardware units on the output signal.
enum AnalogOutputMode {
// Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance.
AnalogOutputMode_DIGITAL_ONLY,
// Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.
AnalogOutputMode_COARSE,
// Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,
// which is passed through the LPF circuit without significant attenuation.
AnalogOutputMode_ACCURATE,
// Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.
// This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs
// compared to a regular LPF FIR implementations.
AnalogOutputMode_OVERSAMPLED
};
enum ReverbMode {
REVERB_MODE_ROOM,
REVERB_MODE_HALL,
REVERB_MODE_PLATE,
REVERB_MODE_TAP_DELAY
};
enum PartialState {
PartialState_INACTIVE,
PartialState_ATTACK,
PartialState_SUSTAIN,
PartialState_RELEASE
};
const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
const Bit8u SYSEX_MDL_MT32 = 0x16;
@ -87,148 +132,10 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
const int MAX_SYSEX_SIZE = 512;
const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices?
const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
struct ControlROMPCMStruct {
Bit8u pos;
Bit8u len;
Bit8u pitchLSB;
Bit8u pitchMSB;
};
struct ControlROMMap {
Bit16u idPos;
Bit16u idLen;
const char *idBytes;
Bit16u pcmTable; // 4 * pcmCount bytes
Bit16u pcmCount;
Bit16u timbreAMap; // 128 bytes
Bit16u timbreAOffset;
bool timbreACompressed;
Bit16u timbreBMap; // 128 bytes
Bit16u timbreBOffset;
bool timbreBCompressed;
Bit16u timbreRMap; // 2 * timbreRCount bytes
Bit16u timbreRCount;
Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
Bit16u rhythmSettingsCount;
Bit16u reserveSettings; // 9 bytes
Bit16u panSettings; // 8 bytes
Bit16u programSettings; // 8 bytes
Bit16u rhythmMaxTable; // 4 bytes
Bit16u patchMaxTable; // 16 bytes
Bit16u systemMaxTable; // 23 bytes
Bit16u timbreMaxTable; // 72 bytes
};
enum MemoryRegionType {
MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
};
enum ReverbMode {
REVERB_MODE_ROOM,
REVERB_MODE_HALL,
REVERB_MODE_PLATE,
REVERB_MODE_TAP_DELAY
};
class MemoryRegion {
private:
Synth *synth;
Bit8u *realMemory;
Bit8u *maxTable;
public:
MemoryRegionType type;
Bit32u startAddr, entrySize, entries;
MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
synth = useSynth;
realMemory = useRealMemory;
maxTable = useMaxTable;
type = useType;
startAddr = useStartAddr;
entrySize = useEntrySize;
entries = useEntries;
}
int lastTouched(Bit32u addr, Bit32u len) const {
return (offset(addr) + len - 1) / entrySize;
}
int firstTouchedOffset(Bit32u addr) const {
return offset(addr) % entrySize;
}
int firstTouched(Bit32u addr) const {
return offset(addr) / entrySize;
}
Bit32u regionEnd() const {
return startAddr + entrySize * entries;
}
bool contains(Bit32u addr) const {
return addr >= startAddr && addr < regionEnd();
}
int offset(Bit32u addr) const {
return addr - startAddr;
}
Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
if (addr + len > regionEnd())
return regionEnd() - addr;
return len;
}
Bit32u next(Bit32u addr, Bit32u len) const {
if (addr + len > regionEnd()) {
return regionEnd() - addr;
}
return 0;
}
Bit8u getMaxValue(int off) const {
if (maxTable == NULL)
return 0xFF;
return maxTable[off % entrySize];
}
Bit8u *getRealMemory() const {
return realMemory;
}
bool isReadable() const {
return getRealMemory() != NULL;
}
void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
};
class PatchTempMemoryRegion : public MemoryRegion {
public:
PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
};
class RhythmTempMemoryRegion : public MemoryRegion {
public:
RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
};
class TimbreTempMemoryRegion : public MemoryRegion {
public:
TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
};
class PatchesMemoryRegion : public MemoryRegion {
public:
PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
};
class TimbresMemoryRegion : public MemoryRegion {
public:
TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
};
class SystemMemoryRegion : public MemoryRegion {
public:
SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
};
class DisplayMemoryRegion : public MemoryRegion {
public:
DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
};
class ResetMemoryRegion : public MemoryRegion {
public:
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
};
class ReportHandler {
friend class Synth;
@ -254,47 +161,6 @@ protected:
virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {}
};
/**
* Used to safely store timestamped MIDI events in a local queue.
*/
struct MidiEvent {
Bit32u shortMessageData;
const Bit8u *sysexData;
Bit32u sysexLength;
Bit32u timestamp;
~MidiEvent();
void setShortMessage(Bit32u shortMessageData, Bit32u timestamp);
void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
};
/**
* Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it.
* It is intended to:
* - get rid of prerenderer while retaining graceful partial abortion
* - add fair emulation of the MIDI interface delays
* - extend the synth interface with the default implementation of a typical rendering loop.
* THREAD SAFETY:
* It is safe to use either in a single thread environment or when there are only two threads - one performs only reading
* and one performs only writing. More complicated usage requires external synchronisation.
*/
class MidiEventQueue {
private:
MidiEvent *ringBuffer;
Bit32u ringBufferSize;
volatile Bit32u startPosition;
volatile Bit32u endPosition;
public:
MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE);
~MidiEventQueue();
void reset();
bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
const MidiEvent *peekMidiEvent();
void dropMidiEvent();
};
class Synth {
friend class Part;
friend class RhythmPart;
@ -335,7 +201,7 @@ private:
volatile Bit32u lastReceivedMIDIEventTimestamp;
volatile Bit32u renderedSampleCount;
MemParams mt32ram, mt32default;
MemParams &mt32ram, &mt32default;
BReverbModel *reverbModels[4];
BReverbModel *reverbModel;
@ -346,12 +212,6 @@ private:
float outputGain;
float reverbOutputGain;
#if MT32EMU_USE_FLOAT_SAMPLES
float effectiveReverbOutputGain;
#else
int effectiveOutputGain;
int effectiveReverbOutputGain;
#endif
bool reversedStereoEnabled;
@ -368,11 +228,12 @@ private:
// We emulate this by delaying new MIDI events processing until abortion finishes.
Poly *abortingPoly;
Bit32u getShortMessageLength(Bit32u msg);
Analog *analog;
Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
void produceLA32Output(Sample *buffer, Bit32u len);
void convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb);
void convertSamplesToOutput(Sample *buffer, Bit32u len);
bool isAbortingPoly() const;
void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
@ -404,13 +265,20 @@ private:
void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]);
void printDebug(const char *fmt, ...);
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
const Part *getPart(unsigned int partNum) const;
public:
static inline Bit16s clipBit16s(Bit32s sample) {
static inline Sample clipSampleEx(SampleEx sampleEx) {
#if MT32EMU_USE_FLOAT_SAMPLES
return sampleEx;
#else
// Clamp values above 32767 to 32767, and values below -32768 to -32768
// FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain.
// The version below is actually a bit faster on my system...
//return ((sample + 0x8000) & ~0xFFFF) ? (sample >> 31) ^ 0x7FFF : (Bit16s)sample;
return ((-0x8000 <= sample) && (sample <= 0x7FFF)) ? (Bit16s)sample : (sample >> 31) ^ 0x7FFF;
//return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx;
return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF;
#endif
}
static inline void muteSampleBuffer(Sample *buffer, Bit32u len) {
@ -426,7 +294,8 @@ public:
#endif
}
static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
static Bit32u getShortMessageLength(Bit32u msg);
static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0);
// Optionally sets callbacks for reporting various errors, information and debug messages
Synth(ReportHandler *useReportHandler = NULL);
@ -435,8 +304,12 @@ public:
// Used to initialise the MT-32. Must be called before any other function.
// Returns true if initialization was sucessful, otherwise returns false.
// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth.
// usePartialCount sets the maximum number of partials playing simultaneously for this session.
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS);
// usePartialCount sets the maximum number of partials playing simultaneously for this session (optional).
// analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional).
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE);
// Overloaded method which opens the synth with default partial count.
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode);
// Closes the MT-32 and deallocates any memory used by the synthesizer
void close(bool forced = false);
@ -444,29 +317,34 @@ public:
// All the enqueued events are processed by the synth immediately.
void flushMIDIQueue();
// Sets size of the internal MIDI event queue.
// Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified.
// The queue is flushed before reallocation.
void setMIDIEventQueueSize(Bit32u);
// Returns the actual queue size being used.
Bit32u setMIDIEventQueueSize(Bit32u);
// Enqueues a MIDI event for subsequent playback.
// The minimum delay involves the delay introduced while the event is transferred via MIDI interface
// and emulation of the MCU busy-loop while it frees partials for use by a new Poly.
// Calls from multiple threads must be synchronised, although,
// no synchronisation is required with the rendering thread.
// The MIDI event will be processed not before the specified timestamp.
// The timestamp is measured as the global rendered sample count since the synth was created.
// The timestamp is measured as the global rendered sample count since the synth was created (at the native sample rate 32000 Hz).
// The minimum delay involves emulation of the delay introduced while the event is transferred via MIDI interface
// and emulation of the MCU busy-loop while it frees partials for use by a new Poly.
// Calls from multiple threads must be synchronised, although, no synchronisation is required with the rendering thread.
// The methods return false if the MIDI event queue is full and the message cannot be enqueued.
// Enqueues a single short MIDI message. The message must contain a status byte.
bool playMsg(Bit32u msg, Bit32u timestamp);
// Enqueues a single well formed System Exclusive MIDI message.
bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
// The MIDI event will be processed ASAP.
// Overloaded methods for the MIDI events to be processed ASAP.
bool playMsg(Bit32u msg);
bool playSysex(const Bit8u *sysex, Bit32u len);
// WARNING:
// The methods below don't ensure minimum 1-sample delay between sequential MIDI events,
// and a sequence of NoteOn and immediately succeeding NoteOff messages is always silent.
// A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering.
// Sends a 4-byte MIDI message to the MT-32 for immediate playback.
// Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte.
void playMsgNow(Bit32u msg);
void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
@ -495,12 +373,17 @@ public:
void setMIDIDelayMode(MIDIDelayMode mode);
MIDIDelayMode getMIDIDelayMode() const;
// Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume.
// Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume,
// it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with setReverbOutputGain()
// it offers to the user a capability to control the gain of reverb and non-reverb output channels independently.
// Ignored in DACInputMode_PURE
void setOutputGain(float);
float getOutputGain() const;
// Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain.
// Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output
// analog circuitry of the hardware units. However, together with setOutputGain() it offers to the user a capability
// to control the gain of reverb and non-reverb output channels independently.
//
// Note: We're currently emulate CM-32L/CM-64 reverb quite accurately and the reverb output level closely
// corresponds to the level of digital capture. Although, according to the CM-64 PCB schematic,
// there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68
@ -512,12 +395,21 @@ public:
void setReversedStereoEnabled(bool enabled);
bool isReversedStereoEnabled();
// Renders samples to the specified output stream.
// The length is in frames, not bytes (in 16-bit stereo,
// one frame is 4 bytes).
// Returns actual sample rate used in emulation of stereo analog circuitry of hardware units.
// See comment for render() below.
unsigned int getStereoOutputSampleRate() const;
// Renders samples to the specified output stream as if they were sampled at the analog stereo output.
// When AnalogOutputMode is set to ACCURATE, the output signal is upsampled to 48 kHz in order
// to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained.
// getStereoOutputSampleRate() can be used to query actual sample rate of the output signal.
// The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes).
void render(Sample *stream, Bit32u len);
// Renders samples to the specified output streams (any or all of which may be NULL).
// Renders samples to the specified output streams as if they appeared at the DAC entrance.
// No further processing performed in analog circuitry emulation is applied to the signal.
// NULL may be specified in place of any or all of the stream buffers.
// The length is in samples, not bytes.
void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
// Returns true when there is at least one active partial, otherwise false.
@ -526,15 +418,28 @@ public:
// Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active.
bool isActive() const;
const Partial *getPartial(unsigned int partialNum) const;
// Returns the maximum number of partials playing simultaneously.
unsigned int getPartialCount() const;
void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
// Fills in current states of all the parts into the array provided. The array must have at least 9 entries to fit values for all the parts.
// If the value returned for a part is true, there is at least one active non-releasing partial playing on this part.
// This info is useful in emulating behaviour of LCD display of the hardware units.
void getPartStates(bool *partStates) const;
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
const Part *getPart(unsigned int partNum) const;
// Fills in current states of all the partials into the array provided. The array must be large enough to accommodate states of all the partials.
void getPartialStates(PartialState *partialStates) const;
// Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough
// to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials.
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
// Returns the number of currently playing notes on the specified part.
unsigned int getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const;
// Returns name of the patch set on the specified part.
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
const char *getPatchName(unsigned int partNumber) const;
void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
};
}

View File

@ -23,6 +23,7 @@
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h"
namespace MT32Emu {

View File

@ -19,6 +19,7 @@
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h"
namespace MT32Emu {

View File

@ -19,6 +19,7 @@
//#include <cstdlib>
#include "mt32emu.h"
#include "internals.h"
namespace MT32Emu {

View File

@ -16,14 +16,15 @@
*/
//#include <cmath>
//#include <cstdlib>
//#include <cstring>
#include "mt32emu.h"
#include "mmath.h"
#include "Tables.h"
namespace MT32Emu {
// UNUSED: const int MIDDLEC = 60;
const Tables &Tables::getInstance() {
static const Tables instance;
return instance;

View File

@ -20,24 +20,11 @@
namespace MT32Emu {
// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator.
// The output from the synth is supposed to be resampled to convert the sample rate.
const unsigned int SAMPLE_RATE = 32000;
// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
const int MIDDLEC = 60;
class Synth;
class Tables {
private:
Tables();
Tables(Tables &);
~Tables() {}
public:
static const Tables &getInstance();

View File

@ -0,0 +1,40 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_TYPES_H
#define MT32EMU_TYPES_H
namespace MT32Emu {
typedef unsigned int Bit32u;
typedef signed int Bit32s;
typedef unsigned short int Bit16u;
typedef signed short int Bit16s;
typedef unsigned char Bit8u;
typedef signed char Bit8s;
#if MT32EMU_USE_FLOAT_SAMPLES
typedef float Sample;
typedef float SampleEx;
#else
typedef Bit16s Sample;
typedef Bit32s SampleEx;
#endif
}
#endif

View File

@ -0,0 +1,83 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_INTERNALS_H
#define MT32EMU_INTERNALS_H
// Debugging
// 0: Standard debug output is not stamped with the rendered sample count
// 1: Standard debug output is stamped with the rendered sample count
// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
// This is important to bear in mind for debug output that occurs during a run.
#define MT32EMU_DEBUG_SAMPLESTAMPS 0
// 0: No debug output for initialisation progress
// 1: Debug output for initialisation progress
#define MT32EMU_MONITOR_INIT 0
// 0: No debug output for MIDI events
// 1: Debug output for weird MIDI events
#define MT32EMU_MONITOR_MIDI 0
// 0: No debug output for note on/off
// 1: Basic debug output for note on/off
// 2: Comprehensive debug output for note on/off
#define MT32EMU_MONITOR_INSTRUMENTS 0
// 0: No debug output for partial allocations
// 1: Show partial stats when an allocation fails
// 2: Show partial stats with every new poly
// 3: Show individual partial allocations/deactivations
#define MT32EMU_MONITOR_PARTIALS 0
// 0: No debug output for sysex
// 1: Basic debug output for sysex
#define MT32EMU_MONITOR_SYSEX 0
// 0: No debug output for sysex writes to the timbre areas
// 1: Debug output with the name and location of newly-written timbres
// 2: Complete dump of timbre parameters for newly-written timbres
#define MT32EMU_MONITOR_TIMBRES 0
// 0: No TVA/TVF-related debug output.
// 1: Shows changes to TVA/TVF target, increment and phase.
#define MT32EMU_MONITOR_TVA 0
#define MT32EMU_MONITOR_TVF 0
// Configuration
// If non-zero, deletes reverb buffers that are not in use to save memory.
// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
#define MT32EMU_REDUCE_REVERB_MEMORY 1
// 0: Maximum speed at the cost of a bit lower emulation accuracy.
// 1: Maximum achievable emulation accuracy.
#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
#include "Structures.h"
#include "Tables.h"
#include "Poly.h"
#include "LA32Ramp.h"
#include "LA32WaveGenerator.h"
#include "TVA.h"
#include "TVP.h"
#include "TVF.h"
#include "Partial.h"
#include "Part.h"
#endif

View File

@ -1,6 +1,7 @@
MODULE := audio/softsynth/mt32
MODULE_OBJS := \
Analog.o \
BReverbModel.o \
LA32Ramp.o \
LA32WaveGenerator.o \

View File

@ -18,63 +18,20 @@
#ifndef MT32EMU_MT32EMU_H
#define MT32EMU_MT32EMU_H
// Debugging
// 0: Standard debug output is not stamped with the rendered sample count
// 1: Standard debug output is stamped with the rendered sample count
// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
// This is important to bear in mind for debug output that occurs during a run.
#define MT32EMU_DEBUG_SAMPLESTAMPS 0
// 0: No debug output for initialisation progress
// 1: Debug output for initialisation progress
#define MT32EMU_MONITOR_INIT 0
// 0: No debug output for MIDI events
// 1: Debug output for weird MIDI events
#define MT32EMU_MONITOR_MIDI 0
// 0: No debug output for note on/off
// 1: Basic debug output for note on/off
// 2: Comprehensive debug output for note on/off
#define MT32EMU_MONITOR_INSTRUMENTS 0
// 0: No debug output for partial allocations
// 1: Show partial stats when an allocation fails
// 2: Show partial stats with every new poly
// 3: Show individual partial allocations/deactivations
#define MT32EMU_MONITOR_PARTIALS 0
// 0: No debug output for sysex
// 1: Basic debug output for sysex
#define MT32EMU_MONITOR_SYSEX 0
// 0: No debug output for sysex writes to the timbre areas
// 1: Debug output with the name and location of newly-written timbres
// 2: Complete dump of timbre parameters for newly-written timbres
#define MT32EMU_MONITOR_TIMBRES 0
// 0: No TVA/TVF-related debug output.
// 1: Shows changes to TVA/TVF target, increment and phase.
#define MT32EMU_MONITOR_TVA 0
#define MT32EMU_MONITOR_TVF 0
// Configuration
// If non-zero, deletes reverb buffers that are not in use to save memory.
// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
#define MT32EMU_REDUCE_REVERB_MEMORY 1
// 0: Maximum speed at the cost of a bit lower emulation accuracy.
// 1: Maximum achievable emulation accuracy.
#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed.
// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
#define MT32EMU_USE_FLOAT_SAMPLES 0
namespace MT32Emu
{
// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator,
// except the emulation of analogue path.
// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate.
const unsigned int SAMPLE_RATE = 32000;
// The default value for the maximum number of partials playing simultaneously.
const unsigned int DEFAULT_MAX_PARTIALS = 32;
@ -97,17 +54,7 @@ const unsigned int MAX_SAMPLES_PER_RUN = 4096;
const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
}
#include "Structures.h"
#include "common/file.h"
#include "Tables.h"
#include "Poly.h"
#include "LA32Ramp.h"
#include "LA32WaveGenerator.h"
#include "TVA.h"
#include "TVP.h"
#include "TVF.h"
#include "Partial.h"
#include "Part.h"
#include "Types.h"
#include "ROMInfo.h"
#include "Synth.h"

View File

@ -39,12 +39,10 @@ Timestamp::Timestamp(uint ms, uint fr) {
Timestamp::Timestamp(uint s, uint frames, uint fr) {
assert(fr > 0);
_secs = s;
_secs = s + (frames / fr);
_framerateFactor = 1000 / Common::gcd<uint>(1000, fr);
_framerate = fr * _framerateFactor;
_numFrames = frames * _framerateFactor;
normalize();
_numFrames = (frames % fr) * _framerateFactor;
}
Timestamp Timestamp::convertToFramerate(uint newFramerate) const {

View File

@ -24,9 +24,12 @@
#if defined(SDL_BACKEND)
#include "common/textconsole.h"
#include "backends/audiocd/sdl/sdl-audiocd.h"
#if !SDL_VERSION_ATLEAST(1, 3, 0)
#include "common/textconsole.h"
SdlAudioCDManager::SdlAudioCDManager()
:
_cdrom(0),
@ -133,4 +136,6 @@ void SdlAudioCDManager::updateCD() {
}
}
#endif // !SDL_VERSION_ATLEAST(1, 3, 0)
#endif

View File

@ -27,6 +27,8 @@
#include "backends/platform/sdl/sdl-sys.h"
#if !SDL_VERSION_ATLEAST(1, 3, 0)
/**
* The SDL audio cd manager. Implements real audio cd playback.
*/
@ -47,4 +49,6 @@ protected:
uint32 _cdEndTime, _cdStopTime;
};
#endif // !SDL_VERSION_ATLEAST(1, 3, 0)
#endif

View File

@ -26,18 +26,48 @@
#include "backends/events/dinguxsdl/dinguxsdl-events.h"
#ifndef GCW0
#define PAD_UP SDLK_UP
#define PAD_DOWN SDLK_DOWN
#define PAD_LEFT SDLK_LEFT
#define PAD_RIGHT SDLK_RIGHT
#define BUT_A SDLK_LCTRL
#define BUT_B SDLK_LALT
#define BUT_X SDLK_SPACE
#define BUT_Y SDLK_LSHIFT
#define BUT_X SDLK_SPACE // BUT_Y in GCW0
#define BUT_Y SDLK_LSHIFT // BUT_X in GCW0
#define BUT_SELECT SDLK_ESCAPE
#define BUT_START SDLK_RETURN
#define TRIG_L SDLK_TAB
#define TRIG_R SDLK_BACKSPACE
#else // GCW0
/******
* GCW0 keymap
* Dingoo button
* A -> Left Button BUT_Y
* B -> right button BUT_B
* X -> ' ' BUT_A '0'
* Y -> '.' BUT_X
* Select -> ESC TRIG_R
* Start -> F5 TRIG_L
* L -> Shift BUT_START
* R -> VK BUT_SELECT
*/
#define PAD_UP SDLK_UP
#define PAD_DOWN SDLK_DOWN
#define PAD_LEFT SDLK_LEFT
#define PAD_RIGHT SDLK_RIGHT
#define BUT_A SDLK_LSHIFT
#define BUT_B SDLK_LALT
#define BUT_X SDLK_SPACE
#define BUT_Y SDLK_LCTRL
#define BUT_SELECT SDLK_BACKSPACE
#define BUT_START SDLK_TAB
#define TRIG_L SDLK_RETURN
#define TRIG_R SDLK_ESCAPE
#endif
bool DINGUXSdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
if (ev.key.keysym.sym == PAD_UP) {

View File

@ -49,22 +49,55 @@
#define JOY_BUT_SPACE 4
#define JOY_BUT_F5 5
#if SDL_VERSION_ATLEAST(2, 0, 0)
static uint32 convUTF8ToUTF32(const char *src) {
uint32 utf32 = 0;
char *dst = SDL_iconv_string(
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
"UTF-32BE",
#else
"UTF-32LE",
#endif
"UTF-8", src, SDL_strlen(src) + 1);
if (dst) {
utf32 = *((uint32 *)dst);
SDL_free(dst);
}
return utf32;
}
#endif
SdlEventSource::SdlEventSource()
: EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0) {
: EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0)
#if SDL_VERSION_ATLEAST(2, 0, 0)
, _queuedFakeKeyUp(false), _fakeKeyUp()
#endif
{
// Reset mouse state
memset(&_km, 0, sizeof(_km));
int joystick_num = ConfMan.getInt("joystick_num");
if (joystick_num > -1) {
if (joystick_num >= 0) {
// Initialize SDL joystick subsystem
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) {
error("Could not initialize SDL: %s", SDL_GetError());
}
// Enable joystick
if (SDL_NumJoysticks() > 0) {
debug("Using joystick: %s", SDL_JoystickName(0));
if (SDL_NumJoysticks() > joystick_num) {
_joystick = SDL_JoystickOpen(joystick_num);
debug("Using joystick: %s",
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_JoystickName(_joystick)
#else
SDL_JoystickName(joystick_num)
#endif
);
} else {
warning("Invalid joystick: %d", joystick_num);
}
}
}
@ -74,21 +107,24 @@ SdlEventSource::~SdlEventSource() {
SDL_JoystickClose(_joystick);
}
int SdlEventSource::mapKey(SDLKey key, SDLMod mod, Uint16 unicode) {
if (key >= SDLK_F1 && key <= SDLK_F9) {
int SdlEventSource::mapKey(SDLKey sdlKey, SDLMod mod, Uint16 unicode) {
Common::KeyCode key = SDLToOSystemKeycode(sdlKey);
if (key >= Common::KEYCODE_F1 && key <= Common::KEYCODE_F9) {
return key - SDLK_F1 + Common::ASCII_F1;
} else if (key >= SDLK_KP0 && key <= SDLK_KP9) {
return key - SDLK_KP0 + '0';
} else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) {
} else if (key >= Common::KEYCODE_KP0 && key <= Common::KEYCODE_KP9) {
return key - Common::KEYCODE_KP0 + '0';
} else if (key >= Common::KEYCODE_UP && key <= Common::KEYCODE_PAGEDOWN) {
return key;
} else if (unicode) {
return unicode;
} else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) {
return key & ~0x20;
} else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) {
} else if (key >= Common::KEYCODE_NUMLOCK && key <= Common::KEYCODE_EURO) {
return 0;
} else {
return key;
}
return key;
}
void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) {
@ -171,7 +207,9 @@ void SdlEventSource::handleKbdMouse() {
_km.y_down_count = 1;
}
SDL_WarpMouse((Uint16)_km.x, (Uint16)_km.y);
if (_graphicsManager) {
_graphicsManager->getWindow()->warpMouseInWindow((Uint16)_km.x, (Uint16)_km.y);
}
}
}
}
@ -338,7 +376,9 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) {
case SDLK_HELP: return Common::KEYCODE_HELP;
case SDLK_PRINT: return Common::KEYCODE_PRINT;
case SDLK_SYSREQ: return Common::KEYCODE_SYSREQ;
#if !SDL_VERSION_ATLEAST(2, 0, 0)
case SDLK_BREAK: return Common::KEYCODE_BREAK;
#endif
case SDLK_MENU: return Common::KEYCODE_MENU;
case SDLK_POWER: return Common::KEYCODE_POWER;
case SDLK_UNDO: return Common::KEYCODE_UNDO;
@ -349,6 +389,16 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) {
bool SdlEventSource::pollEvent(Common::Event &event) {
handleKbdMouse();
#if SDL_VERSION_ATLEAST(2, 0, 0)
// In case we still need to send a key up event for a key down from a
// TEXTINPUT event we do this immediately.
if (_queuedFakeKeyUp) {
event = _fakeKeyUp;
_queuedFakeKeyUp = false;
return true;
}
#endif
// If the screen changed, send an Common::EVENT_SCREEN_CHANGED
int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID();
if (screenID != _lastScreenID) {
@ -385,24 +435,73 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
case SDL_JOYAXISMOTION:
return handleJoyAxisMotion(ev, event);
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_MOUSEWHEEL: {
Sint32 yDir = ev.wheel.y;
#if SDL_VERSION_ATLEAST(2, 0, 4)
if (ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) {
yDir *= -1;
}
#endif
// HACK: It seems we want the mouse coordinates supplied
// with a mouse wheel event. However, SDL2 does not supply
// these, thus we use whatever we got last time. It seems
// these are always stored in _km.x, _km.y.
processMouseEvent(event, _km.x, _km.y);
if (yDir < 0) {
event.type = Common::EVENT_WHEELDOWN;
return true;
} else if (yDir > 0) {
event.type = Common::EVENT_WHEELUP;
return true;
} else {
return false;
}
}
case SDL_TEXTINPUT: {
// When we get a TEXTINPUT event it means we got some user input for
// which no KEYDOWN exists. SDL 1.2 introduces a "fake" key down+up
// in such cases. We will do the same to mimic it's behavior.
event.type = Common::EVENT_KEYDOWN;
event.kbd = Common::KeyState(Common::KEYCODE_INVALID, convUTF8ToUTF32(ev.text.text), 0);
SDLModToOSystemKeyFlags(SDL_GetModState(), event);
// Set the scroll lock sticky flag
if (_scrollLock)
event.kbd.flags |= Common::KBD_SCRL;
// Fake a key up when we have a proper ascii value.
_queuedFakeKeyUp = (event.kbd.ascii != 0);
_fakeKeyUp = event;
_fakeKeyUp.type = Common::EVENT_KEYUP;
return _queuedFakeKeyUp;
}
case SDL_WINDOWEVENT:
switch (ev.window.event) {
case SDL_WINDOWEVENT_EXPOSED:
if (_graphicsManager)
_graphicsManager->notifyVideoExpose();
return false;
case SDL_WINDOWEVENT_RESIZED:
return handleResizeEvent(event, ev.window.data1, ev.window.data2);
default:
return false;
}
#else
case SDL_VIDEOEXPOSE:
if (_graphicsManager)
_graphicsManager->notifyVideoExpose();
return false;
case SDL_VIDEORESIZE:
if (_graphicsManager) {
_graphicsManager->notifyResize(ev.resize.w, ev.resize.h);
// If the screen changed, send an Common::EVENT_SCREEN_CHANGED
int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID();
if (screenID != _lastScreenID) {
_lastScreenID = screenID;
event.type = Common::EVENT_SCREEN_CHANGED;
return true;
}
}
return false;
return handleResizeEvent(event, ev.resize.w, ev.resize.h);
#endif
case SDL_QUIT:
event.type = Common::EVENT_QUIT;
@ -427,7 +526,9 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) {
// Ctrl-m toggles mouse capture
if (event.kbd.hasFlags(Common::KBD_CTRL) && ev.key.keysym.sym == 'm') {
toggleMouseGrab();
if (_graphicsManager) {
_graphicsManager->getWindow()->toggleMouseGrab();
}
return false;
}
@ -470,7 +571,7 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = SDLToOSystemKeycode(ev.key.keysym.sym);
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode);
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, obtainUnicode(ev.key.keysym));
return true;
}
@ -514,7 +615,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_KEYUP;
event.kbd.keycode = SDLToOSystemKeycode(ev.key.keysym.sym);
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode);
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, 0);
// Ctrl-Alt-<key> will change the GFX mode
SDLModToOSystemKeyFlags(mod, event);
@ -750,13 +851,6 @@ bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
return false;
}
void SdlEventSource::toggleMouseGrab() {
if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF)
SDL_WM_GrabInput(SDL_GRAB_ON);
else
SDL_WM_GrabInput(SDL_GRAB_OFF);
}
void SdlEventSource::resetKeyboadEmulation(int16 x_max, int16 y_max) {
_km.x_max = x_max;
_km.y_max = y_max;
@ -764,4 +858,52 @@ void SdlEventSource::resetKeyboadEmulation(int16 x_max, int16 y_max) {
_km.last_time = 0;
}
bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) {
if (_graphicsManager) {
_graphicsManager->notifyResize(w, h);
// If the screen changed, send an Common::EVENT_SCREEN_CHANGED
int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID();
if (screenID != _lastScreenID) {
_lastScreenID = screenID;
event.type = Common::EVENT_SCREEN_CHANGED;
return true;
}
}
return false;
}
uint32 SdlEventSource::obtainUnicode(const SDL_keysym keySym) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Event events[2];
// In SDL2, the unicode field has been removed from the keysym struct.
// Instead a SDL_TEXTINPUT event is generated on key combinations that
// generates unicode.
// Here we peek into the event queue for the event to see if it exists.
int n = SDL_PeepEvents(events, 2, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_TEXTINPUT);
// Make sure that the TEXTINPUT event belongs to this KEYDOWN
// event and not another pending one.
if ((n > 0 && events[0].type == SDL_TEXTINPUT)
|| (n > 1 && events[0].type != SDL_KEYDOWN && events[1].type == SDL_TEXTINPUT)) {
// Remove the text input event we associate with the key press. This
// makes sure we never get any SDL_TEXTINPUT events which do "belong"
// to SDL_KEYDOWN events.
n = SDL_PeepEvents(events, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT);
// This is basically a paranoia safety check because we know there
// must be a text input event in the queue.
if (n > 0) {
return convUTF8ToUTF32(events[0].text.text);
} else {
return 0;
}
} else {
return 0;
}
#else
return keySym.unicode;
#endif
}
#endif

View File

@ -49,11 +49,6 @@ public:
*/
virtual void resetKeyboadEmulation(int16 x_max, int16 y_max);
/**
* Toggles mouse input grab
*/
virtual void toggleMouseGrab();
protected:
/** @name Keyboard mouse emulation
* Disabled by fingolfin 2004-12-18.
@ -130,7 +125,7 @@ protected:
/**
* Maps the ASCII value of key
*/
virtual int mapKey(SDLKey key, SDLMod mod, Uint16 unicode);
int mapKey(SDLKey key, SDLMod mod, Uint16 unicode);
/**
* Configures the key modifiers flags status
@ -141,6 +136,30 @@ protected:
* Translates SDL key codes to OSystem key codes
*/
Common::KeyCode SDLToOSystemKeycode(const SDLKey key);
/**
* Notify graphics manager of a resize request.
*/
bool handleResizeEvent(Common::Event &event, int w, int h);
/**
* Extracts unicode information for the specific key sym.
* May only be used for key down events.
*/
uint32 obtainUnicode(const SDL_keysym keySym);
#if SDL_VERSION_ATLEAST(2, 0, 0)
/**
* Whether _fakeKeyUp contains an event we need to send.
*/
bool _queuedFakeKeyUp;
/**
* A fake key up event when we receive a TEXTINPUT without any previous
* KEYDOWN event.
*/
Common::Event _fakeKeyUp;
#endif
};
#endif

View File

@ -133,7 +133,9 @@ bool SymbianSdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
_currentZone = 0;
event.type = Common::EVENT_MOUSEMOVE;
processMouseEvent(event, _mouseXZone[_currentZone], _mouseYZone[_currentZone]);
SDL_WarpMouse(event.mouse.x, event.mouse.y);
if (_graphicsManager) {
_graphicsManager->getWindow()->warpMouseInWindow(event.mouse.x, event.mouse.y);
}
}
return true;

View File

@ -381,15 +381,17 @@ AbstractFSList AmigaOSFilesystemNode::listVolumes() const {
dosList = IDOS->NextDosEntry(dosList, LDF_VOLUMES);
while (dosList) {
if (dosList->dol_Type == DLT_VOLUME &&
dosList->dol_Name) {
dosList->dol_Name &&
dosList->dol_Port) {
// The original line was
//if (dosList->dol_Type == DLT_VOLUME &&
//dosList->dol_Name &&
//dosList->dol_Task) {
// which errored using SDK 53.24 with a 'struct dosList' has no member called 'dol_Task'
// I removed dol_Task because it's not used anywhere else
// and it neither brought up further errors nor crashes or regressions
// The reason for that was that
// 1) dol_Task wasn't a task pointer, it is a message port instead
// 2) It was redefined to be dol_Port in dos/obsolete.h in afore mentioned SDK
// Copy name to buffer
IDOS->CopyStringBSTRToC(dosList->dol_Name, buffer, MAXPATHLEN);

View File

@ -35,8 +35,8 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{0, 0, 0}
};
DINGUXSdlGraphicsManager::DINGUXSdlGraphicsManager(SdlEventSource *boss)
: SurfaceSdlGraphicsManager(boss) {
DINGUXSdlGraphicsManager::DINGUXSdlGraphicsManager(SdlEventSource *boss, SdlWindow *window)
: SurfaceSdlGraphicsManager(boss, window) {
}
const OSystem::GraphicsMode *DINGUXSdlGraphicsManager::getSupportedGraphicsModes() const {
@ -122,7 +122,7 @@ void DINGUXSdlGraphicsManager::initSize(uint w, uint h) {
if (w > 320 || h > 240) {
setGraphicsMode(GFX_HALF);
setGraphicsModeIntern();
_eventSource->toggleMouseGrab();
_window->toggleMouseGrab();
}
_transactionDetails.sizeChanged = true;

View File

@ -34,7 +34,7 @@ enum {
class DINGUXSdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
DINGUXSdlGraphicsManager(SdlEventSource *boss);
DINGUXSdlGraphicsManager(SdlEventSource *boss, SdlWindow *window);
bool hasFeature(OSystem::Feature f);
void setFeatureState(OSystem::Feature f, bool enable);

View File

@ -35,8 +35,8 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{0, 0, 0}
};
GPHGraphicsManager::GPHGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource) {
GPHGraphicsManager::GPHGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
const OSystem::GraphicsMode *GPHGraphicsManager::getSupportedGraphicsModes() const {
@ -141,7 +141,7 @@ void GPHGraphicsManager::initSize(uint w, uint h, const Graphics::PixelFormat *f
if (w > 320 || h > 240) {
setGraphicsMode(GFX_HALF);
setGraphicsModeIntern();
_eventSource->toggleMouseGrab();
_window->toggleMouseGrab();
}
_videoMode.overlayWidth = 320;

View File

@ -33,7 +33,7 @@ enum {
class GPHGraphicsManager : public SurfaceSdlGraphicsManager {
public:
GPHGraphicsManager(SdlEventSource *boss);
GPHGraphicsManager(SdlEventSource *boss, SdlWindow *window);
bool hasFeature(OSystem::Feature f);
void setFeatureState(OSystem::Feature f, bool enable);

View File

@ -45,8 +45,8 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{0, 0, 0}
};
LinuxmotoSdlGraphicsManager::LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource) {
LinuxmotoSdlGraphicsManager::LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
const OSystem::GraphicsMode *LinuxmotoSdlGraphicsManager::getSupportedGraphicsModes() const {
@ -134,7 +134,7 @@ void LinuxmotoSdlGraphicsManager::initSize(uint w, uint h) {
if (w > 320 || h > 240) {
setGraphicsMode(GFX_HALF);
setGraphicsModeIntern();
_eventSource->toggleMouseGrab();
_window->toggleMouseGrab();
}
_transactionDetails.sizeChanged = true;

View File

@ -27,7 +27,7 @@
class LinuxmotoSdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource);
LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
virtual void initSize(uint w, uint h);
virtual void setGraphicsModeIntern();

View File

@ -21,16 +21,14 @@
*/
#if defined(MAEMO)
#include "SDL_syswm.h"
#include "common/scummsys.h"
#include "backends/platform/maemo/maemo.h"
#include "backends/events/maemosdl/maemosdl-events.h"
#include "backends/graphics/maemosdl/maemosdl-graphics.h"
MaemoSdlGraphicsManager::MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource) {
MaemoSdlGraphicsManager::MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
bool MaemoSdlGraphicsManager::loadGFXMode() {

View File

@ -29,7 +29,7 @@
class MaemoSdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource);
MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
protected:
virtual bool loadGFXMode();

View File

@ -49,9 +49,9 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
_displayWidth(0), _displayHeight(0), _defaultFormat(), _defaultFormatAlpha(),
_gameScreen(nullptr), _gameScreenShakeOffset(0), _overlay(nullptr),
_overlayVisible(false), _cursor(nullptr),
_cursorX(0), _cursorY(0), _cursorHotspotX(0), _cursorHotspotY(0), _cursorHotspotXScaled(0),
_cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0), _cursorKeyColor(0),
_cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false)
_cursorX(0), _cursorY(0), _cursorDisplayX(0),_cursorDisplayY(0), _cursorHotspotX(0), _cursorHotspotY(0),
_cursorHotspotXScaled(0), _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0),
_cursorKeyColor(0), _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false)
#ifdef USE_OSD
, _osdAlpha(0), _osdFadeStartTime(0), _osd(nullptr)
#endif
@ -351,7 +351,7 @@ void OpenGLGraphicsManager::updateScreen() {
return;
}
// Clear the screen buffer
// Clear the screen buffer.
GLCALL(glClear(GL_COLOR_BUFFER_BIT));
const GLfloat shakeOffset = _gameScreenShakeOffset * (GLfloat)_displayHeight / _gameScreen->getHeight();
@ -370,12 +370,42 @@ void OpenGLGraphicsManager::updateScreen() {
// visible.
const GLfloat cursorOffset = _overlayVisible ? 0 : shakeOffset;
_cursor->draw(_cursorX - _cursorHotspotXScaled, _cursorY - _cursorHotspotYScaled + cursorOffset,
_cursor->draw(_cursorDisplayX - _cursorHotspotXScaled,
_cursorDisplayY - _cursorHotspotYScaled + cursorOffset,
_cursorWidthScaled, _cursorHeightScaled);
}
// Fourth step: Draw black borders around the game screen when no overlay
// is visible. This makes sure that the mouse cursor etc. is only drawn
// in the actual game screen area in this case.
if (!_overlayVisible) {
GLCALL(glColor4f(0.0f, 0.0f, 0.0f, 1.0f));
GLCALL(glDisable(GL_TEXTURE_2D));
GLCALL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
// Top border.
drawRect(0, 0, _outputScreenWidth, _displayY);
// Left border.
drawRect(0, 0, _displayX, _outputScreenHeight);
// Bottom border.
const int y = _displayY + _displayHeight;
drawRect(0, y, _outputScreenWidth, _outputScreenHeight - y);
// Right border.
const int x = _displayX + _displayWidth;
drawRect(x, 0, _outputScreenWidth - x, _outputScreenHeight);
GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
GLCALL(glEnable(GL_TEXTURE_2D));
GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
}
#ifdef USE_OSD
// Fourth step: Draw the OSD.
// Fifth step: Draw the OSD.
if (_osdAlpha > 0) {
Common::StackLock lock(_osdMutex);
@ -435,10 +465,16 @@ int16 OpenGLGraphicsManager::getOverlayHeight() {
void OpenGLGraphicsManager::showOverlay() {
_overlayVisible = true;
// Update cursor position.
setMousePosition(_cursorX, _cursorY);
}
void OpenGLGraphicsManager::hideOverlay() {
_overlayVisible = false;
// Update cursor position.
setMousePosition(_cursorX, _cursorY);
}
Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const {
@ -489,7 +525,7 @@ void OpenGLGraphicsManager::warpMouse(int x, int y) {
if (!_overlay) {
return;
}
// It might be confusing that we actually have to handle something
// here when the overlay is visible. This is because for very small
// resolutions we have a minimal overlay size and have to adjust
@ -892,8 +928,8 @@ void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
const int16 width = _gameScreen->getWidth();
const int16 height = _gameScreen->getHeight();
x = (x * width) / _displayWidth;
y = (y * height) / _displayHeight;
x = (x * width) / (int)_displayWidth;
y = (y * height) / (int)_displayHeight;
// Make sure we only supply valid coordinates.
x = CLIP<int16>(x, 0, width - 1);
@ -901,6 +937,19 @@ void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
}
}
void OpenGLGraphicsManager::setMousePosition(int x, int y) {
_cursorX = x;
_cursorY = y;
if (_overlayVisible) {
_cursorDisplayX = x;
_cursorDisplayY = y;
} else {
_cursorDisplayX = CLIP<int>(x, _displayX, _displayX + _displayWidth - 1);
_cursorDisplayY = CLIP<int>(y, _displayY, _displayY + _displayHeight - 1);
}
}
Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &format, bool wantAlpha) {
GLenum glIntFormat, glFormat, glType;
if (format.bytesPerPixel == 1) {
@ -1044,8 +1093,11 @@ void OpenGLGraphicsManager::recalculateDisplayArea() {
}
// We center the screen in the middle for now.
_displayX = (_outputScreenWidth - _displayWidth ) / 2;
_displayY = (_outputScreenHeight - _displayHeight) / 2;
_displayX = (_outputScreenWidth - _displayWidth ) / 2;
_displayY = (_outputScreenHeight - _displayHeight) / 2;
// Update the cursor position to adjust for new display area.
setMousePosition(_cursorX, _cursorY);
}
void OpenGLGraphicsManager::updateCursorPalette() {
@ -1163,4 +1215,20 @@ void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const
delete[] pixels;
}
void OpenGLGraphicsManager::drawRect(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
if (w < 0 || h < 0) {
return;
}
const GLfloat vertices[4*2] = {
x, y,
x + w, y,
x, y + h,
x + w, y + h
};
GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices));
GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
}
} // End of namespace OpenGL

View File

@ -155,7 +155,7 @@ protected:
* @param x X coordinate in physical coordinates.
* @param y Y coordinate in physical coordinates.
*/
void setMousePosition(int x, int y) { _cursorX = x; _cursorY = y; }
void setMousePosition(int x, int y);
/**
* Query the mouse position in physical coordinates.
@ -393,6 +393,16 @@ private:
*/
int _cursorY;
/**
* X coordinate used for drawing the cursor.
*/
int _cursorDisplayX;
/**
* Y coordinate used for drawing the cursor.
*/
int _cursorDisplayY;
/**
* The X offset for the cursor hotspot in unscaled coordinates.
*/
@ -454,6 +464,11 @@ private:
*/
byte _cursorPalette[3 * 256];
/**
* Draws a rectangle
*/
void drawRect(GLfloat x, GLfloat y, GLfloat w, GLfloat h);
#ifdef USE_OSD
//
// OSD

View File

@ -28,8 +28,13 @@
#include "common/translation.h"
#endif
OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource)
: SdlGraphicsManager(eventSource), _lastVideoModeLoad(0), _hwScreen(nullptr), _lastRequestedWidth(0), _lastRequestedHeight(0),
OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource, SdlWindow *window)
: SdlGraphicsManager(eventSource, window), _lastRequestedHeight(0),
#if SDL_VERSION_ATLEAST(2, 0, 0)
_glContext(),
#else
_lastVideoModeLoad(0), _hwScreen(nullptr),
#endif
_graphicsScale(2), _ignoreLoadVideoMode(false), _gotResize(false), _wantsFullScreen(false), _ignoreResizeEvents(0),
_desiredFullscreenWidth(0), _desiredFullscreenHeight(0) {
// Setup OpenGL attributes for SDL
@ -40,16 +45,40 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Retrieve a list of working fullscreen modes
#if SDL_VERSION_ATLEAST(2, 0, 0)
const int numModes = SDL_GetNumDisplayModes(0);
for (int i = 0; i < numModes; ++i) {
SDL_DisplayMode mode;
if (SDL_GetDisplayMode(0, i, &mode)) {
continue;
}
_fullscreenVideoModes.push_back(VideoMode(mode.w, mode.h));
}
#else
const SDL_Rect *const *availableModes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
if (availableModes != (void *)-1) {
// TODO: NULL means that there are no fullscreen modes supported. We
// should probably use this information and disable any fullscreen support
// in this case.
if (availableModes != NULL && availableModes != (void *)-1) {
for (;*availableModes; ++availableModes) {
const SDL_Rect *mode = *availableModes;
_fullscreenVideoModes.push_back(VideoMode(mode->w, mode->h));
}
}
#endif
// Sort the modes in ascending order.
Common::sort(_fullscreenVideoModes.begin(), _fullscreenVideoModes.end());
// Sort the modes in ascending order.
Common::sort(_fullscreenVideoModes.begin(), _fullscreenVideoModes.end());
// Strip duplicates in video modes.
for (uint i = 0; i + 1 < _fullscreenVideoModes.size();) {
if (_fullscreenVideoModes[i] == _fullscreenVideoModes[i + 1]) {
_fullscreenVideoModes.remove_at(i);
} else {
++i;
}
}
// In case SDL is fine with every mode we will force the desktop mode.
@ -108,7 +137,7 @@ void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
case OSystem::kFeatureIconifyWindow:
if (enable) {
SDL_WM_IconifyWindow();
_window->iconifyWindow();
}
break;
@ -120,11 +149,19 @@ void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
bool OpenGLSdlGraphicsManager::getFeatureState(OSystem::Feature f) {
switch (f) {
case OSystem::kFeatureFullscreenMode:
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
return (SDL_GetWindowFlags(_window->getSDLWindow()) & SDL_WINDOW_FULLSCREEN) != 0;
} else {
return _wantsFullScreen;
}
#else
if (_hwScreen) {
return (_hwScreen->flags & SDL_FULLSCREEN) != 0;
} else {
return _wantsFullScreen;
}
#endif
default:
return OpenGLGraphicsManager::getFeatureState(f);
@ -201,13 +238,20 @@ void OpenGLSdlGraphicsManager::updateScreen() {
OpenGLGraphicsManager::updateScreen();
// Swap OpenGL buffers
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GL_SwapWindow(_window->getSDLWindow());
#else
SDL_GL_SwapBuffers();
#endif
}
void OpenGLSdlGraphicsManager::notifyVideoExpose() {
}
void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
setActualScreenSize(width, height);
#else
if (!_ignoreResizeEvents && _hwScreen && !(_hwScreen->flags & SDL_FULLSCREEN)) {
// We save that we handled a resize event here. We need to know this
// so we do not overwrite the users requested window size whenever we
@ -218,6 +262,7 @@ void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height)
g_system->quit();
}
}
#endif
}
void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) {
@ -229,7 +274,7 @@ void OpenGLSdlGraphicsManager::notifyMousePos(Common::Point mouse) {
}
void OpenGLSdlGraphicsManager::setInternalMousePosition(int x, int y) {
SDL_WarpMouse(x, y);
_window->warpMouseInWindow(x, y);
}
bool OpenGLSdlGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format) {
@ -282,7 +327,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
if (!_fullscreenVideoModes.empty()) {
VideoModeArray::const_iterator i = _fullscreenVideoModes.end();
--i;
_desiredFullscreenWidth = i->width;
_desiredFullscreenHeight = i->height;
} else {
@ -300,6 +345,58 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
height = _desiredFullscreenHeight;
}
// This is pretty confusing since RGBA8888 talks about the memory
// layout here. This is a different logical layout depending on
// whether we run on little endian or big endian. However, we can
// only safely assume that RGBA8888 in memory layout is supported.
// Thus, we chose this one.
const Graphics::PixelFormat rgba8888 =
#ifdef SCUMM_LITTLE_ENDIAN
Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
#else
Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_glContext) {
notifyContextDestroy();
SDL_GL_DeleteContext(_glContext);
_glContext = nullptr;
}
_window->destroyWindow();
uint32 flags = SDL_WINDOW_OPENGL;
if (_wantsFullScreen) {
flags |= SDL_WINDOW_FULLSCREEN;
} else {
flags |= SDL_WINDOW_RESIZABLE;
}
if (!_window->createWindow(width, height, flags)) {
// We treat fullscreen requests as a "hint" for now. This means in
// case it is not available we simply ignore it.
if (_wantsFullScreen) {
_window->createWindow(width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
}
if (!_window->getSDLWindow()) {
return false;
}
}
_glContext = SDL_GL_CreateContext(_window->getSDLWindow());
if (!_glContext) {
return false;
}
notifyContextCreate(rgba8888, rgba8888);
int actualWidth, actualHeight;
getWindowDimensions(&actualWidth, &actualHeight);
setActualScreenSize(actualWidth, actualHeight);
return true;
#else
// WORKAROUND: Working around infamous SDL bugs when switching
// resolutions too fast. This might cause the event system to supply
// incorrect mouse position events otherwise.
@ -341,17 +438,6 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
_lastVideoModeLoad = SDL_GetTicks();
if (_hwScreen) {
// This is pretty confusing since RGBA8888 talks about the memory
// layout here. This is a different logical layout depending on
// whether we run on little endian or big endian. However, we can
// only safely assume that RGBA8888 in memory layout is supported.
// Thus, we chose this one.
const Graphics::PixelFormat rgba8888 =
#ifdef SCUMM_LITTLE_ENDIAN
Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
#else
Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
#endif
notifyContextCreate(rgba8888, rgba8888);
setActualScreenSize(_hwScreen->w, _hwScreen->h);
}
@ -363,6 +449,21 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
_ignoreResizeEvents = 10;
return _hwScreen != nullptr;
#endif
}
void OpenGLSdlGraphicsManager::getWindowDimensions(int *width, int *height) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GetWindowSize(_window->getSDLWindow(), width, height);
#else
if (width) {
*width = _hwScreen->w;
}
if (height) {
*height = _hwScreen->h;
}
#endif
}
bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
@ -456,7 +557,9 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
// Calculate the next scaling setting. We approximate the
// current scale setting in case the user resized the
// window. Then we apply the direction change.
_graphicsScale = MAX<int>(_hwScreen->w / _lastRequestedWidth, _hwScreen->h / _lastRequestedHeight);
int windowWidth = 0, windowHeight = 0;
getWindowDimensions(&windowWidth, &windowHeight);
_graphicsScale = MAX<int>(windowWidth / _lastRequestedWidth, windowHeight / _lastRequestedHeight);
_graphicsScale = MAX<int>(_graphicsScale + direction, 1);
// Since we overwrite a user resize here we reset its
@ -472,7 +575,9 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
}
#ifdef USE_OSD
const Common::String osdMsg = Common::String::format("Resolution: %dx%d", _hwScreen->w, _hwScreen->h);
int windowWidth = 0, windowHeight = 0;
getWindowDimensions(&windowWidth, &windowHeight);
const Common::String osdMsg = Common::String::format("Resolution: %dx%d", windowWidth, windowHeight);
displayMessageOnOSD(osdMsg.c_str());
#endif

View File

@ -32,7 +32,7 @@
class OpenGLSdlGraphicsManager : public OpenGL::OpenGLGraphicsManager, public SdlGraphicsManager, public Common::EventObserver {
public:
OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource);
OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource, SdlWindow *window);
virtual ~OpenGLSdlGraphicsManager();
// GraphicsManager API
@ -68,8 +68,14 @@ protected:
private:
bool setupMode(uint width, uint height);
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GLContext _glContext;
#else
uint32 _lastVideoModeLoad;
SDL_Surface *_hwScreen;
#endif
void getWindowDimensions(int *width, int *height);
uint _lastRequestedWidth;
uint _lastRequestedHeight;

View File

@ -32,8 +32,8 @@
static SDL_Cursor *hiddenCursor;
OPGraphicsManager::OPGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource) {
OPGraphicsManager::OPGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
bool OPGraphicsManager::loadGFXMode() {

View File

@ -32,7 +32,7 @@ enum {
class OPGraphicsManager : public SurfaceSdlGraphicsManager {
public:
OPGraphicsManager(SdlEventSource *sdlEventSource);
OPGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
bool loadGFXMode();
void unloadGFXMode();

View File

@ -28,8 +28,8 @@
#include "backends/events/samsungtvsdl/samsungtvsdl-events.h"
#include "backends/graphics/samsungtvsdl/samsungtvsdl-graphics.h"
SamsungTVSdlGraphicsManager::SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource) {
SamsungTVSdlGraphicsManager::SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
bool SamsungTVSdlGraphicsManager::hasFeature(OSystem::Feature f) {

View File

@ -29,7 +29,7 @@
class SamsungTVSdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource);
SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
bool hasFeature(OSystem::Feature f);
void setFeatureState(OSystem::Feature f, bool enable);

View File

@ -22,10 +22,12 @@
#include "backends/graphics/sdl/sdl-graphics.h"
#include "backends/platform/sdl/sdl-sys.h"
#include "backends/events/sdl/sdl-events.h"
#include "common/textconsole.h"
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source)
: _eventSource(source) {
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source, SdlWindow *window)
: _eventSource(source), _window(window) {
}
SdlGraphicsManager::~SdlGraphicsManager() {
@ -38,3 +40,36 @@ void SdlGraphicsManager::activateManager() {
void SdlGraphicsManager::deactivateManager() {
_eventSource->setGraphicsManager(0);
}
SdlGraphicsManager::State SdlGraphicsManager::getState() {
State state;
state.screenWidth = getWidth();
state.screenHeight = getHeight();
state.aspectRatio = getFeatureState(OSystem::kFeatureAspectRatioCorrection);
state.fullscreen = getFeatureState(OSystem::kFeatureFullscreenMode);
state.cursorPalette = getFeatureState(OSystem::kFeatureCursorPalette);
#ifdef USE_RGB_COLOR
state.pixelFormat = getScreenFormat();
#endif
return state;
}
bool SdlGraphicsManager::setState(const State &state) {
beginGFXTransaction();
#ifdef USE_RGB_COLOR
initSize(state.screenWidth, state.screenHeight, &state.pixelFormat);
#else
initSize(state.screenWidth, state.screenHeight, 0);
#endif
setFeatureState(OSystem::kFeatureAspectRatioCorrection, state.aspectRatio);
setFeatureState(OSystem::kFeatureFullscreenMode, state.fullscreen);
setFeatureState(OSystem::kFeatureCursorPalette, state.cursorPalette);
if (endGFXTransaction() != OSystem::kTransactionSuccess) {
return false;
} else {
return true;
}
}

View File

@ -24,6 +24,7 @@
#define BACKENDS_GRAPHICS_SDL_SDLGRAPHICS_H
#include "backends/graphics/graphics.h"
#include "backends/platform/sdl/sdl-window.h"
#include "common/rect.h"
@ -36,7 +37,7 @@ class SdlEventSource;
*/
class SdlGraphicsManager : virtual public GraphicsManager {
public:
SdlGraphicsManager(SdlEventSource *source);
SdlGraphicsManager(SdlEventSource *source, SdlWindow *window);
virtual ~SdlGraphicsManager();
/**
@ -91,8 +92,39 @@ public:
*/
virtual void notifyMousePos(Common::Point mouse) = 0;
/**
* A (subset) of the graphic manager's state. This is used when switching
* between different SDL graphic managers on runtime.
*/
struct State {
int screenWidth, screenHeight;
bool aspectRatio;
bool fullscreen;
bool cursorPalette;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat pixelFormat;
#endif
};
/**
* Queries the current state of the graphic manager.
*/
State getState();
/**
* Setup a basic state of the graphic manager.
*/
bool setState(const State &state);
/**
* Queries the SDL window.
*/
SdlWindow *getWindow() const { return _window; }
protected:
SdlEventSource *_eventSource;
SdlWindow *_window;
};
#endif

View File

@ -116,13 +116,19 @@ static AspectRatio getDesiredAspectRatio() {
}
#endif
SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource)
SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
:
SdlGraphicsManager(sdlEventSource),
SdlGraphicsManager(sdlEventSource, window),
#ifdef USE_OSD
_osdSurface(0), _osdAlpha(SDL_ALPHA_TRANSPARENT), _osdFadeStartTime(0),
#endif
_hwscreen(0), _screen(0), _tmpscreen(0),
_hwscreen(0),
#if SDL_VERSION_ATLEAST(2, 0, 0)
_renderer(nullptr), _screenTexture(nullptr),
#else
_originalBitsPerPixel(0),
#endif
_screen(0), _tmpscreen(0),
#ifdef USE_RGB_COLOR
_screenFormat(Graphics::PixelFormat::createFormatCLUT8()),
_cursorFormat(Graphics::PixelFormat::createFormatCLUT8()),
@ -235,7 +241,7 @@ void SurfaceSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
break;
case OSystem::kFeatureIconifyWindow:
if (enable)
SDL_WM_IconifyWindow();
_window->iconifyWindow();
break;
default:
break;
@ -681,12 +687,22 @@ static void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &w
const int w = width;
const int h = height;
int bestW = 0, bestH = 0;
uint bestMetric = (uint)-1; // Metric is wasted space
#if SDL_VERSION_ATLEAST(2, 0, 0)
const int numModes = SDL_GetNumDisplayModes(0);
SDL_DisplayMode modeData, *mode = &modeData;
for (int i = 0; i < numModes; ++i) {
if (SDL_GetDisplayMode(0, i, &modeData)) {
continue;
}
#else
SDL_Rect const* const*availableModes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_SWSURFACE); //TODO : Maybe specify a pixel format
assert(availableModes);
const SDL_Rect *bestMode = NULL;
uint bestMetric = (uint)-1; // Metric is wasted space
while (const SDL_Rect *mode = *availableModes++) {
#endif
if (mode->w < w)
continue;
if (mode->h < h)
@ -699,15 +715,23 @@ static void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &w
continue;
bestMetric = metric;
bestMode = mode;
}
bestW = mode->w;
bestH = mode->h;
if (!bestMode) {
// Make editors a bit more happy by having the same amount of closing as
// opening curley braces.
#if SDL_VERSION_ATLEAST(2, 0, 0)
}
#else
}
#endif
if (!bestW || !bestH) {
warning("Unable to enforce the desired aspect ratio");
return;
}
width = bestMode->w;
height = bestMode->h;
width = bestW;
height = bestH;
}
bool SurfaceSdlGraphicsManager::loadGFXMode() {
@ -774,7 +798,15 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
_hwscreen = g_eventRec.getSurface(_videoMode.hardwareWidth, _videoMode.hardwareHeight);
} else
#endif
{
{
// Save the original bpp to be able to restore the video mode on unload
#if !SDL_VERSION_ATLEAST(2, 0, 0)
if (_originalBitsPerPixel == 0) {
const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
_originalBitsPerPixel = videoInfo->vfmt->BitsPerPixel;
}
#endif
_hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16,
_videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
);
@ -876,6 +908,10 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
_screen = NULL;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
deinitializeRenderer();
#endif
if (_hwscreen) {
SDL_FreeSurface(_hwscreen);
_hwscreen = NULL;
@ -903,6 +939,13 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
}
#endif
DestroyScalers();
#if !SDL_VERSION_ATLEAST(2, 0, 0)
// Reset video mode to original
// This will ensure that any new graphic manager will use the initial BPP when listing available modes
if (_originalBitsPerPixel != 0)
SDL_SetVideoMode(_videoMode.screenWidth, _videoMode.screenHeight, _originalBitsPerPixel, _videoMode.fullscreen ? (SDL_FULLSCREEN | SDL_SWSURFACE) : SDL_SWSURFACE);
#endif
}
bool SurfaceSdlGraphicsManager::hotswapGFXMode() {
@ -1443,6 +1486,9 @@ void SurfaceSdlGraphicsManager::setPalette(const byte *colors, uint start, uint
base[i].r = b[0];
base[i].g = b[1];
base[i].b = b[2];
#if SDL_VERSION_ATLEAST(2, 0, 0)
base[i].a = 255;
#endif
}
if (start < _paletteDirtyStart)
@ -1481,6 +1527,9 @@ void SurfaceSdlGraphicsManager::setCursorPalette(const byte *colors, uint start,
base[i].r = b[0];
base[i].g = b[1];
base[i].b = b[2];
#if SDL_VERSION_ATLEAST(2, 0, 0)
base[i].a = 255;
#endif
}
_cursorPaletteDisabled = false;
@ -1710,7 +1759,7 @@ void SurfaceSdlGraphicsManager::warpMouse(int x, int y) {
int y1 = y;
// Don't change actual mouse position, when mouse is outside of our window (in case of windowed mode)
if (!(SDL_GetAppState( ) & SDL_APPMOUSEFOCUS)) {
if (!_window->hasMouseFocus()) {
setMousePos(x, y); // but change game cursor position
return;
}
@ -1720,9 +1769,9 @@ void SurfaceSdlGraphicsManager::warpMouse(int x, int y) {
if (_mouseCurState.x != x || _mouseCurState.y != y) {
if (!_overlayVisible)
SDL_WarpMouse(x * _videoMode.scaleFactor, y1 * _videoMode.scaleFactor);
_window->warpMouseInWindow(x * _videoMode.scaleFactor, y1 * _videoMode.scaleFactor);
else
SDL_WarpMouse(x, y1);
_window->warpMouseInWindow(x, y1);
// SDL_WarpMouse() generates a mouse movement event, so
// setMousePos() would be called eventually. However, the
@ -2317,4 +2366,52 @@ void SurfaceSdlGraphicsManager::notifyMousePos(Common::Point mouse) {
setMousePos(mouse.x, mouse.y);
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
void SurfaceSdlGraphicsManager::deinitializeRenderer() {
SDL_DestroyTexture(_screenTexture);
_screenTexture = nullptr;
SDL_DestroyRenderer(_renderer);
_renderer = nullptr;
_window->destroyWindow();
}
SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) {
deinitializeRenderer();
if (!_window->createWindow(width, height, (flags & SDL_FULLSCREEN) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)) {
return nullptr;
}
_renderer = SDL_CreateRenderer(_window->getSDLWindow(), -1, 0);
if (!_renderer) {
deinitializeRenderer();
return nullptr;
}
_screenTexture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, width, height);
if (!_screenTexture) {
deinitializeRenderer();
return nullptr;
}
SDL_Surface *screen = SDL_CreateRGBSurface(0, width, height, 16, 0xF800, 0x7E0, 0x1F, 0);
if (!screen) {
deinitializeRenderer();
return nullptr;
} else {
return screen;
}
}
void SurfaceSdlGraphicsManager::SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects) {
SDL_UpdateTexture(_screenTexture, nullptr, screen->pixels, screen->pitch);
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _screenTexture, NULL, NULL);
SDL_RenderPresent(_renderer);
}
#endif // SDL_VERSION_ATLEAST(2, 0, 0)
#endif

View File

@ -77,7 +77,7 @@ public:
*/
class SurfaceSdlGraphicsManager : public SdlGraphicsManager, public Common::EventObserver {
public:
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource);
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
virtual ~SurfaceSdlGraphicsManager();
virtual void activateManager();
@ -166,6 +166,17 @@ protected:
/** Hardware screen */
SDL_Surface *_hwscreen;
#if SDL_VERSION_ATLEAST(2, 0, 0)
/* SDL2 features a different API for 2D graphics. We create a wrapper
* around this API to keep the code paths as close as possible. */
SDL_Renderer *_renderer;
SDL_Texture *_screenTexture;
void deinitializeRenderer();
SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
#endif
/** Unseen game screen */
SDL_Surface *_screen;
#ifdef USE_RGB_COLOR
@ -225,6 +236,9 @@ protected:
};
VideoState _videoMode, _oldVideoMode;
// Original BPP to restore the video mode on unload
uint8 _originalBitsPerPixel;
/** Force full redraw on next updateScreen */
bool _forceFull;

View File

@ -27,8 +27,8 @@
#include "backends/graphics/symbiansdl/symbiansdl-graphics.h"
#include "backends/platform/symbian/src/SymbianActions.h"
SymbianSdlGraphicsManager::SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource) {
SymbianSdlGraphicsManager::SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
int SymbianSdlGraphicsManager::getDefaultGraphicsMode() const {

View File

@ -27,7 +27,7 @@
class SymbianSdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource);
SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
public:
virtual bool hasFeature(OSystem::Feature f);

View File

@ -42,8 +42,8 @@
#include "backends/platform/wince/CEScaler.h"
#include "backends/platform/wince/CEgui/ItemAction.h"
WINCESdlGraphicsManager::WINCESdlGraphicsManager(SdlEventSource *sdlEventSource)
: SurfaceSdlGraphicsManager(sdlEventSource),
WINCESdlGraphicsManager::WINCESdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window),
_panelInitialized(false), _noDoubleTapRMB(false), _noDoubleTapPT(false),
_toolbarHighDrawn(false), _newOrientation(0), _orientationLandscape(0),
_panelVisible(true), _saveActiveToolbar(NAME_MAIN_PANEL), _panelStateForced(false),

View File

@ -41,7 +41,7 @@ extern bool _hasSmartphoneResolution;
class WINCESdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
WINCESdlGraphicsManager(SdlEventSource *sdlEventSource);
WINCESdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL);

View File

@ -37,7 +37,7 @@ namespace Common {
struct HardwareInput;
class Keymap;
#define ACTION_ID_SIZE (4)
#define ACTION_ID_SIZE (5)
struct KeyActionEntry {
const KeyState ks;

View File

@ -93,10 +93,12 @@ void Log::print(const char *message, const bool printTime) {
void Log::printTimeStamp() {
TimeDate date;
int curMonth;
_system->getTimeAndDate(date);
curMonth = date.tm_mon + 1; // month is base 0, we need base 1 (1 = january and so on)
_stream->writeString(Common::String::format("[%d-%02d-%02d %02d:%02d:%02d] ",
date.tm_year + 1900, date.tm_mon, date.tm_mday,
date.tm_year + 1900, curMonth, date.tm_mday,
date.tm_hour, date.tm_min, date.tm_sec));
}

View File

@ -53,7 +53,11 @@ void DoubleBufferSDLMixerManager::startAudio() {
_soundThreadIsRunning = true;
// Finally start the thread
#if SDL_VERSION_ATLEAST(2, 0, 0)
_soundThread = SDL_CreateThread(mixerProducerThreadEntry, "ScummVM Double Buffer Mixer", this);
#else
_soundThread = SDL_CreateThread(mixerProducerThreadEntry, this);
#endif
SdlMixerManager::startAudio();
}

View File

@ -57,10 +57,14 @@ void SdlMixerManager::init() {
error("Could not initialize SDL: %s", SDL_GetError());
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
const char *sdlDriverName = SDL_GetCurrentAudioDriver();
#else
const int maxNameLen = 20;
char sdlDriverName[maxNameLen];
sdlDriverName[0] = '\0';
SDL_AudioDriverName(sdlDriverName, maxNameLen);
#endif
debug(1, "Using SDL Audio Driver \"%s\"", sdlDriverName);
// Get the desired audio specs

View File

@ -74,9 +74,11 @@ MODULE_OBJS += \
# SDL 1.3 removed audio CD support
ifndef USE_SDL13
ifndef USE_SDL2
MODULE_OBJS += \
audiocd/sdl/sdl-audiocd.o
endif
endif
ifdef USE_OPENGL
MODULE_OBJS += \

View File

@ -396,12 +396,6 @@ void OSystem_Android::initBackend() {
EventsBaseBackend::initBackend();
}
void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const {
ENTER();
JNI::getPluginDirectories(dirs);
}
bool OSystem_Android::hasFeature(Feature f) {
return (f == kFeatureFullscreenMode ||
f == kFeatureAspectRatioCorrection ||
@ -600,10 +594,4 @@ Common::String OSystem_Android::getSystemProperty(const char *name) const {
return Common::String(value, len);
}
#ifdef DYNAMIC_MODULES
void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const {
((OSystem_Android *)g_system)->addPluginDirectories(dirs);
}
#endif
#endif

View File

@ -96,13 +96,6 @@ extern void checkGlError(const char *expr, const char *file, int line);
#define GLTHREADCHECK do { } while (false)
#endif
#ifdef DYNAMIC_MODULES
class AndroidPluginProvider : public POSIXPluginProvider {
protected:
virtual void addCustomDirectories(Common::FSList &dirs) const;
};
#endif
class OSystem_Android : public EventsBaseBackend, public PaletteManager {
private:
// passed from the dark side
@ -177,7 +170,6 @@ public:
virtual ~OSystem_Android();
virtual void initBackend();
void addPluginDirectories(Common::FSList &dirs) const;
void enableZoning(bool enable) { _enable_zoning = enable; }
virtual bool hasFeature(Feature f);

View File

@ -2,200 +2,106 @@
# These must be incremented for each market upload
ANDROID_VERSIONCODE = 6
ANDROID_PLUGIN_VERSIONCODE = 6
JAVA_FILES = \
ScummVM.java \
ScummVMEvents.java \
ScummVMEventsHoneycomb.java \
ScummVMApplication.java \
ScummVMActivity.java \
EditableSurfaceView.java \
MouseHelper.java \
Unpacker.java
ANDROID_TARGET_VERSION = 14
JAVA_FILES_PLUGIN = \
PluginProvider.java
JAVA_FILES_GEN = \
Manifest.java \
R.java
NDK_BUILD = $(ANDROID_NDK)/ndk-build APP_ABI=$(ABI)
SDK_ANDROID = $(ANDROID_SDK)/tools/android
PATH_DIST = $(srcdir)/dists/android
PATH_RESOURCES = $(PATH_DIST)/res
PORT_DISTFILES = $(PATH_DIST)/README.Android
DIST_JAVA_SRC_DIR = $(srcdir)/backends/platform/android/org
RESOURCES = \
$(PATH_RESOURCES)/values/strings.xml \
$(PATH_RESOURCES)/values/margins.xml \
$(PATH_RESOURCES)/values-television/margins.xml \
$(PATH_RESOURCES)/layout/main.xml \
$(PATH_RESOURCES)/layout/splash.xml \
$(PATH_RESOURCES)/drawable/gradient.xml \
$(PATH_RESOURCES)/drawable/scummvm.png \
$(PATH_RESOURCES)/drawable/scummvm_big.png \
$(PATH_RESOURCES)/drawable-xhdpi/ouya_icon.png
$(PATH_BUILD_RES)/values/strings.xml \
$(PATH_BUILD_RES)/values-television/margins.xml \
$(PATH_BUILD_RES)/layout/main.xml \
$(PATH_BUILD_RES)/drawable/scummvm.png \
$(PATH_BUILD_RES)/drawable/scummvm_big.png \
$(PATH_BUILD_RES)/drawable-xhdpi/ouya_icon.png
PLUGIN_RESOURCES = \
$(PATH_RESOURCES)/values/strings.xml \
$(PATH_RESOURCES)/drawable/scummvm.png
# FIXME: find/mark plugin entry points and add all this back again:
#LDFLAGS += -Wl,--gc-sections
#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden
AAPT = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/aapt
ADB = $(ANDROID_SDK)/platform-tools/adb
DX = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/dx
APKBUILDER = java -Xmx128M -classpath $(ANDROID_SDK)/tools/lib/sdklib.jar com.android.sdklib.build.ApkBuilderMain
JAVAC ?= javac
JAVACFLAGS = -source 1.5 -target 1.5
ANDROID_JAR = $(ANDROID_SDK)/platforms/android-14/android.jar
DIST_ANDROID_MK = $(PATH_DIST)/jni/Android.mk
DIST_BUILD_XML = $(PATH_DIST)/custom_rules.xml
PATH_BUILD = ./build.tmp
PATH_BUILD_ASSETS = $(PATH_BUILD)/assets
PATH_BUILD_CLASSES_MAIN_TOP = $(PATH_BUILD)/classes.main
PATH_BUILD_CLASSES_PLUGIN_TOP = $(PATH_BUILD)/classes.plugin
PATH_STAGE_PREFIX = build.stage
PATH_STAGE_MAIN = $(PATH_STAGE_PREFIX).main
PATH_REL = org/scummvm/scummvm
PATH_SRC_TOP = $(srcdir)/backends/platform/android
PATH_SRC = $(PATH_SRC_TOP)/$(PATH_REL)
PATH_GEN_TOP = $(PATH_BUILD)/java
PATH_GEN = $(PATH_GEN_TOP)/$(PATH_REL)
PATH_CLASSES_MAIN = $(PATH_BUILD_CLASSES_MAIN_TOP)/$(PATH_REL)
PATH_CLASSES_PLUGIN = $(PATH_BUILD_CLASSES_PLUGIN_TOP)/$(PATH_REL)
PATH_BUILD_RES = $(PATH_BUILD)/res
PATH_BUILD_LIBSCUMMVM = $(PATH_BUILD)/lib/$(ABI)/libscummvm.so
FILE_MANIFEST_SRC = $(srcdir)/dists/android/AndroidManifest.xml
FILE_MANIFEST = $(PATH_BUILD)/AndroidManifest.xml
FILE_DEX = $(PATH_BUILD)/classes.dex
FILE_DEX_PLUGIN = $(PATH_BUILD)/plugins/classes.dex
FILE_RESOURCES = resources.ap_
FILE_RESOURCES_MAIN = $(PATH_BUILD)/$(FILE_RESOURCES)
SRC_GEN = $(addprefix $(PATH_GEN)/, $(JAVA_FILES_GEN))
APK_MAIN = ScummVM-debug.apk
APK_MAIN_RELEASE = ScummVM-release-unsigned.apk
CLASSES_MAIN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES:%.java=%.class))
CLASSES_GEN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES_GEN:%.java=%.class))
CLASSES_PLUGIN = $(addprefix $(PATH_CLASSES_PLUGIN)/, $(JAVA_FILES_PLUGIN:%.java=%.class))
APK_MAIN = scummvm.apk
APK_PLUGINS = $(patsubst plugins/lib%.so, scummvm-engine-%.apk, $(PLUGINS))
$(FILE_MANIFEST): $(FILE_MANIFEST_SRC)
$(FILE_MANIFEST): $(FILE_MANIFEST_SRC) | $(PATH_BUILD)
@$(MKDIR) -p $(@D)
sed "s/@ANDROID_VERSIONCODE@/$(ANDROID_VERSIONCODE)/" < $< > $@
$(SRC_GEN): $(FILE_MANIFEST) $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR)
@$(MKDIR) -p $(PATH_GEN_TOP)
$(AAPT) package -m -J $(PATH_GEN_TOP) -M $< -S $(PATH_RESOURCES) -I $(ANDROID_JAR)
$(PATH_CLASSES_MAIN)/%.class: $(PATH_GEN)/%.java $(SRC_GEN)
@$(MKDIR) -p $(@D)
$(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $<
$(PATH_CLASSES_MAIN)/%.class: $(PATH_SRC)/%.java $(SRC_GEN)
@$(MKDIR) -p $(@D)
$(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP):$(PATH_GEN_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $<
$(PATH_CLASSES_PLUGIN)/%.class: $(PATH_SRC)/%.java
@$(MKDIR) -p $(@D)
$(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_PLUGIN_TOP) -bootclasspath $(ANDROID_JAR) $<
$(FILE_DEX): $(CLASSES_MAIN) $(CLASSES_GEN)
$(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_MAIN_TOP)
$(FILE_DEX_PLUGIN): $(CLASSES_PLUGIN)
@$(MKDIR) -p $(@D)
$(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_PLUGIN_TOP)
$(PATH_BUILD)/%/AndroidManifest.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml
@$(MKDIR) -p $(@D)
$(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-manifest.xml $(ANDROID_PLUGIN_VERSIONCODE) $@
$(PATH_STAGE_PREFIX).%/res/values/strings.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml
@$(MKDIR) -p $(@D)
$(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-strings.xml $(ANDROID_PLUGIN_VERSIONCODE) $@
$(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png: $(PATH_RESOURCES)/drawable/scummvm.png
$(PATH_BUILD)/res/%: $(PATH_DIST)/res/% | $(PATH_BUILD)
@$(MKDIR) -p $(@D)
$(CP) $< $@
$(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA)
$(PATH_BUILD)/libs/%: $(PATH_DIST)/libs/% | $(PATH_BUILD)
@$(MKDIR) -p $(@D)
$(CP) $< $@
$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_SHADERS) $(DIST_BUILD_XML) | $(PATH_BUILD)
$(INSTALL) -d $(PATH_BUILD_ASSETS)
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(PATH_BUILD_ASSETS)/
work_dir=`pwd`; \
for i in $(PATH_BUILD_ASSETS)/*.zip; do \
echo "recompress $$i"; \
cd "$$work_dir"; \
$(RM) -rf $(PATH_BUILD_ASSETS)/tmp; \
$(MKDIR) $(PATH_BUILD_ASSETS)/tmp; \
unzip -q $$i -d $(PATH_BUILD_ASSETS)/tmp; \
cd $(PATH_BUILD_ASSETS)/tmp; \
zip -r ../`basename $$i` *; \
done
@$(RM) -rf $(PATH_BUILD_ASSETS)/tmp
$(AAPT) package -f -0 zip -M $< -S $(PATH_RESOURCES) -A $(PATH_BUILD_ASSETS) -I $(ANDROID_JAR) -F $@
$(INSTALL) -d $(PATH_BUILD)/jni
$(INSTALL) -c -m 644 $(DIST_ANDROID_MK) $(PATH_BUILD)/jni
$(INSTALL) -c -m 644 $(DIST_BUILD_XML) $(PATH_BUILD)
$(PATH_BUILD)/%/$(FILE_RESOURCES): $(PATH_BUILD)/%/AndroidManifest.xml $(PATH_STAGE_PREFIX).%/res/values/strings.xml $(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png plugins/lib%.so $(ANDROID_JAR)
$(AAPT) package -f -M $< -S $(PATH_STAGE_PREFIX).$*/res -I $(ANDROID_JAR) -F $@
$(PATH_BUILD): $(DIST_ANDROID_MK)
$(MKDIR) -p $(PATH_BUILD) $(PATH_BUILD)/res
$(MKDIR) -p $(PATH_BUILD)/libs
# Package installer won't delete old libscummvm.so on upgrade so
# replace it with a zero size file
$(APK_MAIN): $(EXECUTABLE) $(FILE_RESOURCES_MAIN) $(FILE_DEX)
$(INSTALL) -d $(PATH_STAGE_MAIN)/common/lib/armeabi
touch $(PATH_STAGE_MAIN)/common/lib/armeabi/libscummvm.so
$(INSTALL) -d $(PATH_STAGE_MAIN)/common/mylib/armeabi
$(INSTALL) -c -m 644 libscummvm.so $(PATH_STAGE_MAIN)/common/mylib/armeabi/
$(STRIP) $(PATH_STAGE_MAIN)/common/mylib/armeabi/libscummvm.so
$(APKBUILDER) $@ -z $(FILE_RESOURCES_MAIN) -f $(FILE_DEX) -rf $(PATH_STAGE_MAIN)/common || { $(RM) $@; exit 1; }
$(PATH_BUILD_LIBSCUMMVM): libscummvm.so | $(PATH_BUILD)
$(INSTALL) -c -m 644 libscummvm.so $(PATH_BUILD)
$(STRIP) $(PATH_BUILD)/libscummvm.so
cd $(PATH_BUILD); $(NDK_BUILD)
scummvm-engine-%.apk: plugins/lib%.so $(PATH_BUILD)/%/$(FILE_RESOURCES) $(FILE_DEX_PLUGIN)
$(INSTALL) -d $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/
$(INSTALL) -c -m 644 plugins/lib$*.so $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/
$(STRIP) $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/lib$*.so
$(APKBUILDER) $@ -z $(PATH_BUILD)/$*/$(FILE_RESOURCES) -f $(FILE_DEX_PLUGIN) -rf $(PATH_STAGE_PREFIX).$*/apk || { $(RM) $@; exit 1; }
$(PATH_BUILD_RES): $(RESOURCES) | $(PATH_BUILD)
all: $(APK_MAIN) $(APK_PLUGINS)
setupapk: $(FILE_MANIFEST) $(PATH_BUILD_RES) $(PATH_BUILD_ASSETS) $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD)
$(SDK_ANDROID) update project -p $(PATH_BUILD) -t android-$(ANDROID_TARGET_VERSION) -n ScummVM
$(APK_MAIN): setupapk | $(PATH_BUILD)
(cd $(PATH_BUILD); ant debug -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))")
$(CP) $(PATH_BUILD)/bin/ScummVM-debug.apk $@
$(APK_MAIN_RELEASE): setupapk | $(PATH_BUILD)
(cd $(PATH_BUILD); ant release -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))")
$(CP) $(PATH_BUILD)/bin/ScummVM-release-unsigned.apk $@
all: $(APK_MAIN)
clean: androidclean
androidclean:
@$(RM) -rf $(PATH_BUILD) $(PATH_STAGE_PREFIX).* *.apk release
@$(RM) -rf $(PATH_BUILD) *.apk release
# remove debugging signature
release/%.apk: %.apk
@$(MKDIR) -p $(@D)
@$(RM) $@
$(CP) $< $@.tmp
zip -d $@.tmp META-INF/\*
jarsigner $(JARSIGNER_FLAGS) $@.tmp release
zipalign 4 $@.tmp $@
$(RM) $@.tmp
androidrelease: $(addprefix release/, $(APK_MAIN) $(APK_PLUGINS))
androidrelease: $(APK_MAIN_RELEASE)
androidtestmain: $(APK_MAIN)
$(ADB) install -r $(APK_MAIN)
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.Unpacker
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.ScummVMActivity
androidtest: $(APK_MAIN) $(APK_PLUGINS)
androidtest: $(APK_MAIN)
@set -e; for apk in $^; do \
$(ADB) install -r $$apk; \
done
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.Unpacker
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.ScummVMActivity
# used by buildbot!
androiddistdebug: all
$(MKDIR) debug
$(CP) $(APK_MAIN) $(APK_PLUGINS) debug/
$(CP) $(APK_MAIN) debug/
for i in $(DIST_FILES_DOCS) $(PORT_DISTFILES); do \
sed 's/$$/\r/' < $$i > debug/`basename $$i`.txt; \
done
.PHONY: androidrelease androidtest
.PHONY: androidrelease androidtest $(PATH_BUILD_SRC)

View File

@ -295,7 +295,6 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
jclass cls = env->GetObjectClass(_assetfd);
MID_close = env->GetMethodID(cls, "close", "()V");
assert(MID_close);
env->DeleteLocalRef(cls);
jmethodID MID_getStartOffset =
env->GetMethodID(cls, "getStartOffset", "()J");
@ -321,6 +320,8 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
_fd = env->GetIntField(javafd, FID_descriptor);
env->DeleteLocalRef(javafd);
env->DeleteLocalRef(cls);
}
AssetFdReadStream::~AssetFdReadStream() {

View File

@ -79,7 +79,6 @@ jmethodID JNI::_MID_displayMessageOnOSD = 0;
jmethodID JNI::_MID_setWindowCaption = 0;
jmethodID JNI::_MID_showVirtualKeyboard = 0;
jmethodID JNI::_MID_getSysArchives = 0;
jmethodID JNI::_MID_getPluginDirectories = 0;
jmethodID JNI::_MID_initSurface = 0;
jmethodID JNI::_MID_deinitSurface = 0;
@ -293,46 +292,6 @@ void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
}
}
void JNI::getPluginDirectories(Common::FSList &dirs) {
JNIEnv *env = JNI::getEnv();
jobjectArray array =
(jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories);
if (env->ExceptionCheck()) {
LOGE("Error finding plugin directories");
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
jsize size = env->GetArrayLength(array);
for (jsize i = 0; i < size; ++i) {
jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
if (path_obj == 0)
continue;
const char *path = env->GetStringUTFChars(path_obj, 0);
if (path == 0) {
LOGE("Error getting string characters from plugin directory");
env->ExceptionClear();
env->DeleteLocalRef(path_obj);
continue;
}
dirs.push_back(Common::FSNode(path));
env->ReleaseStringUTFChars(path_obj, path);
env->DeleteLocalRef(path_obj);
}
}
bool JNI::initSurface() {
JNIEnv *env = JNI::getEnv();
@ -454,7 +413,6 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
FIND_METHOD(, showVirtualKeyboard, "(Z)V");
FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
FIND_METHOD(, getPluginDirectories, "()[Ljava/lang/String;");
FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
FIND_METHOD(, deinitSurface, "()V");
@ -543,10 +501,6 @@ jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
env->DeleteLocalRef(arg);
}
#ifdef DYNAMIC_MODULES
PluginManager::instance().addPluginProvider(new AndroidPluginProvider());
#endif
LOGI("Entering scummvm_main with %d args", argc);
res = scummvm_main(argc, argv);

View File

@ -55,7 +55,6 @@ public:
static void setReadyForEvents(bool ready);
static void getPluginDirectories(Common::FSList &dirs);
static void setWindowCaption(const char *caption);
static void getDPI(float *values);
static void displayMessageOnOSD(const char *msg);
@ -93,7 +92,6 @@ private:
static jmethodID _MID_setWindowCaption;
static jmethodID _MID_showVirtualKeyboard;
static jmethodID _MID_getSysArchives;
static jmethodID _MID_getPluginDirectories;
static jmethodID _MID_initSurface;
static jmethodID _MID_deinitSurface;

View File

@ -1,63 +0,0 @@
package org.scummvm.scummvm;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.util.ArrayList;
public class PluginProvider extends BroadcastReceiver {
private final static String LOG_TAG = "ScummVM";
public final static String META_UNPACK_LIB =
"org.scummvm.scummvm.meta.UNPACK_LIB";
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(ScummVMApplication.ACTION_PLUGIN_QUERY))
return;
Bundle extras = getResultExtras(true);
final ActivityInfo info;
final PackageInfo pinfo;
try {
info = context.getPackageManager()
.getReceiverInfo(new ComponentName(context, this.getClass()),
PackageManager.GET_META_DATA);
pinfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Error finding my own info?", e);
return;
}
String host_version = extras.getString(ScummVMApplication.EXTRA_VERSION);
if (!pinfo.versionName.equals(host_version)) {
Log.e(LOG_TAG, "Plugin version " + pinfo.versionName + " is not equal to ScummVM version " + host_version);
return;
}
String mylib = info.metaData.getString(META_UNPACK_LIB);
if (mylib != null) {
ArrayList<String> all_libs =
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
all_libs.add(new Uri.Builder()
.scheme("plugin")
.authority(context.getPackageName())
.path(mylib)
.toString());
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
all_libs);
}
setResultExtras(extras);
}
}

View File

@ -54,7 +54,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
abstract protected void getDPI(float[] values);
abstract protected void displayMessageOnOSD(String msg);
abstract protected void setWindowCaption(String caption);
abstract protected String[] getPluginDirectories();
abstract protected void showVirtualKeyboard(boolean enable);
abstract protected String[] getSysArchives();
@ -444,10 +443,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
}
}
File cache_dir = ScummVMApplication.getLastCacheDir();
String libname = System.mapLibraryName("scummvm");
File libpath = new File(cache_dir, libname);
System.load(libpath.getPath());
System.loadLibrary("scummvm");
}
}

View File

@ -83,13 +83,6 @@ public class ScummVMActivity extends Activity {
});
}
@Override
protected String[] getPluginDirectories() {
String[] dirs = new String[1];
dirs[0] = ScummVMApplication.getLastCacheDir().getPath();
return dirs;
}
@Override
protected void showVirtualKeyboard(final boolean enable) {
runOnUiThread(new Runnable() {

View File

@ -1,31 +0,0 @@
package org.scummvm.scummvm;
import android.app.Application;
import java.io.File;
public class ScummVMApplication extends Application {
public final static String ACTION_PLUGIN_QUERY = "org.scummvm.scummvm.action.PLUGIN_QUERY";
public final static String EXTRA_UNPACK_LIBS = "org.scummvm.scummvm.extra.UNPACK_LIBS";
public final static String EXTRA_VERSION = "org.scummvm.scummvm.extra.VERSION";
private static File _cache_dir;
@Override
public void onCreate() {
super.onCreate();
// This is still on /data :(
_cache_dir = getCacheDir();
// This is mounted noexec :(
//cache_dir = new File(Environment.getExternalStorageDirectory(),
// "/.ScummVM.tmp");
// This is owned by download manager and requires special
// permissions to access :(
//cache_dir = Environment.getDownloadCacheDirectory();
}
public static File getLastCacheDir() {
return _cache_dir;
}
}

View File

@ -1,388 +0,0 @@
package org.scummvm.scummvm;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.ProgressBar;
import java.io.IOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
public class Unpacker extends Activity {
protected final static String LOG_TAG = "ScummVM";
// TODO don't hardcode this
private final static boolean PLUGINS_ENABLED = false;
private final static String META_NEXT_ACTIVITY =
"org.scummvm.unpacker.nextActivity";
private ProgressBar mProgress;
private File mUnpackDest; // location to unpack into
private AsyncTask<String, Integer, Void> mUnpacker;
private final static int REQUEST_MARKET = 1;
// Android 3.1+ only
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32;
private static class UnpackJob {
public ZipFile zipfile;
public Set<String> paths;
public UnpackJob(ZipFile zipfile, Set<String> paths) {
this.zipfile = zipfile;
this.paths = paths;
}
public long UnpackSize() {
long size = 0;
for (String path: paths) {
ZipEntry entry = zipfile.getEntry(path);
if (entry != null) size += entry.getSize();
}
return size;
}
}
private class UnpackTask extends AsyncTask<String, Integer, Void> {
@Override
protected void onProgressUpdate(Integer... progress) {
mProgress.setIndeterminate(false);
mProgress.setMax(progress[1]);
mProgress.setProgress(progress[0]);
mProgress.postInvalidate();
}
@Override
protected void onPostExecute(Void result) {
Bundle md = getMetaData();
String nextActivity = md.getString(META_NEXT_ACTIVITY);
if (nextActivity != null) {
final ComponentName cn =
ComponentName.unflattenFromString(nextActivity);
if (cn != null) {
final Intent origIntent = getIntent();
Intent intent = new Intent();
intent.setComponent(cn);
if (origIntent.getExtras() != null)
intent.putExtras(origIntent.getExtras());
intent.putExtra(Intent.EXTRA_INTENT, origIntent);
intent.setDataAndType(origIntent.getData(),
origIntent.getType());
//intent.fillIn(getIntent(), 0);
intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Log.i(LOG_TAG,
"Starting next activity with intent " + intent);
startActivity(intent);
} else {
Log.w(LOG_TAG,
"Unable to extract a component name from " + nextActivity);
}
}
finish();
}
@Override
protected Void doInBackground(String... all_libs) {
// This will contain all unpack jobs
Map<String, UnpackJob> unpack_jobs =
new HashMap<String, UnpackJob>(all_libs.length);
// This will contain all unpack filenames (so we can
// detect stale files in the unpack directory)
Set<String> all_files = new HashSet<String>(all_libs.length);
for (String lib: all_libs) {
final Uri uri = Uri.parse(lib);
final String pkg = uri.getAuthority();
final String path = uri.getPath().substring(1); // skip first /
all_files.add(new File(path).getName());
UnpackJob job = unpack_jobs.get(pkg);
if (job == null) {
try {
// getPackageResourcePath is hidden in Context,
// but exposed in ContextWrapper...
ContextWrapper context =
new ContextWrapper(createPackageContext(pkg, 0));
ZipFile zipfile =
new ZipFile(context.getPackageResourcePath());
job = new UnpackJob(zipfile, new HashSet<String>(1));
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Package " + pkg +
" not found", e);
continue;
} catch (IOException e) {
// FIXME: show some sort of GUI error dialog
Log.e(LOG_TAG,
"Error opening ZIP for package " + pkg, e);
continue;
}
unpack_jobs.put(pkg, job);
}
job.paths.add(path);
}
// Delete stale filenames from mUnpackDest
for (File file: mUnpackDest.listFiles()) {
if (!all_files.contains(file.getName())) {
Log.i(LOG_TAG,
"Deleting stale cached file " + file);
file.delete();
}
}
int total_size = 0;
for (UnpackJob job: unpack_jobs.values())
total_size += job.UnpackSize();
publishProgress(0, total_size);
mUnpackDest.mkdirs();
int progress = 0;
for (UnpackJob job: unpack_jobs.values()) {
try {
ZipFile zipfile = job.zipfile;
for (String path: job.paths) {
ZipEntry zipentry = zipfile.getEntry(path);
if (zipentry == null)
throw new FileNotFoundException(
"Couldn't find " + path + " in zip");
File dest = new File(mUnpackDest, new File(path).getName());
if (dest.exists() &&
dest.lastModified() == zipentry.getTime() &&
dest.length() == zipentry.getSize()) {
// Already unpacked
progress += zipentry.getSize();
} else {
if (dest.exists())
Log.d(LOG_TAG,
"Replacing " + dest.getPath() +
" old.mtime=" + dest.lastModified() +
" new.mtime=" + zipentry.getTime() +
" old.size=" + dest.length() +
" new.size=" + zipentry.getSize());
else
Log.i(LOG_TAG,
"Extracting " + zipentry.getName() +
" from " + zipfile.getName() +
" to " + dest.getPath());
long next_update = progress;
InputStream in = zipfile.getInputStream(zipentry);
OutputStream out = new FileOutputStream(dest);
int len;
byte[] buffer = new byte[4096];
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
progress += len;
if (progress >= next_update) {
publishProgress(progress, total_size);
// Arbitrary limit of 2% update steps
next_update += total_size / 50;
}
}
in.close();
out.close();
dest.setLastModified(zipentry.getTime());
}
publishProgress(progress, total_size);
}
zipfile.close();
} catch (IOException e) {
// FIXME: show some sort of GUI error dialog
Log.e(LOG_TAG, "Error unpacking plugin", e);
}
}
if (progress != total_size)
Log.d(LOG_TAG, "Ended with progress " + progress +
" != total size " + total_size);
setResult(RESULT_OK);
return null;
}
}
private class PluginBroadcastReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction()
.equals(ScummVMApplication.ACTION_PLUGIN_QUERY)) {
Log.e(LOG_TAG,
"Received unexpected action " + intent.getAction());
return;
}
Bundle extras = getResultExtras(false);
if (extras == null) {
// Nothing for us to do.
Unpacker.this.setResult(RESULT_OK);
finish();
}
ArrayList<String> unpack_libs =
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
if (unpack_libs != null && !unpack_libs.isEmpty()) {
final String[] libs =
unpack_libs.toArray(new String[unpack_libs.size()]);
mUnpacker = new UnpackTask().execute(libs);
}
}
}
private void initPlugins() {
Bundle extras = new Bundle(1);
ArrayList<String> unpack_libs = new ArrayList<String>(1);
// This is the common ScummVM code (not really a "plugin" as such)
unpack_libs.add(new Uri.Builder()
.scheme("plugin")
.authority(getPackageName())
.path("mylib/armeabi/libscummvm.so")
.toString());
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
unpack_libs);
final PackageInfo info;
try {
info = getPackageManager().getPackageInfo(getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Error finding my own info?", e);
return;
}
extras.putString(ScummVMApplication.EXTRA_VERSION, info.versionName);
Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
// Android 3.1 defaults to FLAG_EXCLUDE_STOPPED_PACKAGES, and since
// none of our plugins will ever be running, that is not helpful
intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
sendOrderedBroadcast(intent, Manifest.permission.SCUMMVM_PLUGIN,
new PluginBroadcastReciever(),
null, RESULT_OK, null, extras);
}
@Override
public void onCreate(Bundle b) {
super.onCreate(b);
mUnpackDest = ScummVMApplication.getLastCacheDir();
setContentView(R.layout.splash);
mProgress = (ProgressBar)findViewById(R.id.progress);
setResult(RESULT_CANCELED);
tryUnpack();
}
private void tryUnpack() {
Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
List<ResolveInfo> plugins = getPackageManager()
.queryBroadcastReceivers(intent, 0);
if (PLUGINS_ENABLED && plugins.isEmpty()) {
// No plugins installed
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.no_plugins_title)
.setMessage(R.string.no_plugins_found)
.setIcon(android.R.drawable.ic_dialog_alert)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
finish();
}
})
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
final Uri uri = Uri.parse("market://search?q=ScummVM plugin");
final Intent market_intent = new Intent(Intent.ACTION_VIEW, uri);
if (getPackageManager().resolveActivity(market_intent, 0) != null) {
alert.setPositiveButton(R.string.to_market,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
try {
startActivityForResult(market_intent,
REQUEST_MARKET);
} catch (ActivityNotFoundException e) {
Log.e(LOG_TAG,
"Error starting market", e);
}
}
});
}
alert.show();
} else {
// Already have at least one plugin installed
initPlugins();
}
}
@Override
public void onStop() {
if (mUnpacker != null)
mUnpacker.cancel(true);
super.onStop();
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
switch (requestCode) {
case REQUEST_MARKET:
if (resultCode != RESULT_OK)
Log.w(LOG_TAG, "Market returned " + resultCode);
tryUnpack();
break;
}
}
private Bundle getMetaData() {
try {
ActivityInfo ai = getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
return ai.metaData;
} catch (PackageManager.NameNotFoundException e) {
Log.w(LOG_TAG, "Unable to find my own meta-data", e);
return new Bundle();
}
}
}

View File

@ -33,7 +33,7 @@ void OSystem_SDL_Dingux::initBackend() {
// Create the graphics manager
if (_graphicsManager == 0) {
_graphicsManager = new DINGUXSdlGraphicsManager(_eventSource);
_graphicsManager = new DINGUXSdlGraphicsManager(_eventSource, _window);
}
// Call parent implementation of this method

View File

@ -1,6 +1,7 @@
DINGUX_EXE_STRIPPED := scummvm_stripped$(EXEEXT)
bundle_name = dingux-dist/scummvm
gcw0_bundle = gcw0-opk
all: $(DINGUX_EXE_STRIPPED)
@ -30,3 +31,37 @@ endif
$(CP) $(srcdir)/backends/platform/dingux/scummvm.gpe $(bundle_name)/
$(CP) $(srcdir)/backends/platform/dingux/README.DINGUX $(bundle_name)/
$(CP) $(srcdir)/backends/platform/dingux/scummvm.png $(bundle_name)/
# Special target for generationg GCW-Zero OPK bundle
$(gcw0_bundle): all
$(MKDIR) $(gcw0_bundle)
$(CP) $(DIST_FILES_DOCS) $(gcw0_bundle)/
$(MKDIR) $(gcw0_bundle)/themes
$(CP) $(DIST_FILES_THEMES) $(gcw0_bundle)/themes/
ifdef DIST_FILES_ENGINEDATA
$(MKDIR) $(gcw0_bundle)/engine-data
$(CP) $(DIST_FILES_ENGINEDATA) $(gcw0_bundle)/engine-data/
endif
ifdef DYNAMIC_MODULES
$(MKDIR) $(gcw0_bundle)/plugins
$(CP) $(PLUGINS) $(gcw0_bundle)/plugins/
endif
$(CP) $(EXECUTABLE) $(gcw0_bundle)/scummvm
$(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip $(gcw0_bundle)/
$(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_small.zip $(gcw0_bundle)/
$(CP) $(srcdir)/dists/gcw0/scummvm.png $(gcw0_bundle)/
$(CP) $(srcdir)/dists/gcw0/default.gcw0.desktop $(gcw0_bundle)/
$(CP) $(srcdir)/dists/gcw0/scummvmrc $(gcw0_bundle)/
$(CP) $(srcdir)/dists/gcw0/scummvm.sh $(gcw0_bundle)/
gcw0-opk-unstripped: $(gcw0_bundle)
$(CP) $(PLUGINS) $(gcw0_bundle)/plugins/
$(CP) $(EXECUTABLE) $(gcw0_bundle)/scummvm
./dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
gcw-opk: $(gcw0_bundle)
$(STRIP) $(gcw0_bundle)/plugins/*
$(STRIP) $(gcw0_bundle)/scummvm
./dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm

View File

@ -159,7 +159,7 @@ void OSystem_GPH::initBackend() {
// Create the graphics manager
if (_graphicsManager == 0) {
_graphicsManager = new GPHGraphicsManager(_eventSource);
_graphicsManager = new GPHGraphicsManager(_eventSource, _window);
}
/* Pass to POSIX method to do the heavy lifting */

View File

@ -31,7 +31,7 @@ void OSystem_LINUXMOTO::initBackend() {
_eventSource = new LinuxmotoSdlEventSource();
if (_graphicsManager == 0)
_graphicsManager = new LinuxmotoSdlGraphicsManager(_eventSource);
_graphicsManager = new LinuxmotoSdlGraphicsManager(_eventSource, _window);
// Call parent implementation of this method
OSystem_POSIX::initBackend();

View File

@ -35,10 +35,6 @@
#include "common/textconsole.h"
#include "common/translation.h"
#include <SDL/SDL_syswm.h>
#include <X11/Xutil.h>
namespace Maemo {
OSystem_SDL_Maemo::OSystem_SDL_Maemo()
@ -84,6 +80,15 @@ static void registerDefaultKeyBindings(Common::KeymapperDefaultBindings *_keymap
}
#endif
void OSystem_SDL_Maemo::init() {
// Use an iconless window for Maemo
// also N900 is hit by SDL_WM_SetIcon bug (window cannot receive input)
// http://bugzilla.libsdl.org/show_bug.cgi?id=586
_window = new SdlIconlessWindow();
OSystem_POSIX::init();
}
void OSystem_SDL_Maemo::initBackend() {
ConfMan.registerDefault("fullscreen", true);
ConfMan.registerDefault("aspect_ratio", true);
@ -93,7 +98,7 @@ void OSystem_SDL_Maemo::initBackend() {
_eventSource = new MaemoSdlEventSource();
if (_graphicsManager == 0)
_graphicsManager = new MaemoSdlGraphicsManager(_eventSource);
_graphicsManager = new MaemoSdlGraphicsManager(_eventSource, _window);
if (_eventObserver == 0)
_eventObserver = new MaemoSdlEventObserver((MaemoSdlEventSource *)_eventSource);
@ -178,12 +183,6 @@ const Maemo::Model OSystem_SDL_Maemo::detectModel() {
return *model;
}
void OSystem_SDL_Maemo::setupIcon() {
// no Maemo version needs setupIcon
// also N900 is hit by SDL_WM_SetIcon bug (window cannot receive input)
// http://bugzilla.libsdl.org/show_bug.cgi?id=586
}
#ifdef ENABLE_KEYMAPPER
static const Common::KeyTableEntry maemoKeys[] = {
// Function keys

View File

@ -36,11 +36,11 @@ public:
OSystem_SDL_Maemo();
~OSystem_SDL_Maemo();
virtual void init();
virtual void initBackend();
virtual void quit();
virtual void fatalError();
virtual void setWindowCaption(const char *caption);
virtual void setupIcon();
#ifdef ENABLE_KEYMAPPER
virtual Common::HardwareInputSet *getHardwareInputSet();
virtual Common::Keymap *getGlobalKeymap();

View File

@ -147,7 +147,7 @@ void OSystem_OP::initBackend() {
// Create the graphics manager
if (_graphicsManager == 0) {
_graphicsManager = new OPGraphicsManager(_eventSource);
_graphicsManager = new OPGraphicsManager(_eventSource, _window);
}
/* Pass to POSIX method to do the heavy lifting */

View File

@ -40,7 +40,7 @@ void OSystem_SDL_SamsungTV::initBackend() {
_eventSource = new SamsungTVSdlEventSource();
if (_graphicsManager == 0)
_graphicsManager = new SamsungTVSdlGraphicsManager(_eventSource);
_graphicsManager = new SamsungTVSdlGraphicsManager(_eventSource, _window);
// Call parent implementation of this method
OSystem_POSIX::initBackend();

View File

@ -47,6 +47,9 @@ OSystem_MacOSX::OSystem_MacOSX()
}
void OSystem_MacOSX::init() {
// Use an iconless window on OS X, as we use a nicer external icon there.
_window = new SdlIconlessWindow();
#if defined(USE_TASKBAR)
// Initialize taskbar manager
_taskbarManager = new MacOSXTaskbarManager();
@ -101,10 +104,6 @@ void OSystem_MacOSX::addSysArchivesToSearchSet(Common::SearchSet &s, int priorit
}
}
void OSystem_MacOSX::setupIcon() {
// Don't set icon on OS X, as we use a nicer external icon there.
}
bool OSystem_MacOSX::hasFeature(Feature f) {
if (f == kFeatureDisplayLogFile)
return true;

View File

@ -38,7 +38,6 @@ public:
virtual void init();
virtual void initBackend();
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
virtual void setupIcon();
};
#endif

View File

@ -1,7 +1,8 @@
MODULE := backends/platform/sdl
MODULE_OBJS := \
sdl.o
sdl.o \
sdl-window.o
ifdef POSIX
MODULE_OBJS += \
@ -19,6 +20,7 @@ endif
ifdef WIN32
MODULE_OBJS += \
win32/win32-main.o \
win32/win32-window.o \
win32/win32.o
endif

View File

@ -52,12 +52,84 @@ typedef struct { int FAKE; } FAKE_FILE;
#define strncasecmp FAKE_strncasecmp
#endif
// HACK: SDL might include windows.h which defines its own ARRAYSIZE.
// However, we want to use the version from common/util.h. Thus, we make sure
// that we actually have this definition after including the SDL headers.
#if defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
#define HACK_REDEFINE_ARRAYSIZE
#undef ARRAYSIZE
#endif
// HACK to fix compilation with SDL 2.0 in MSVC.
// In SDL 2.0, intrin.h is now included in SDL_cpuinfo.h, which includes
// setjmp.h. SDL_cpuinfo.h is included from SDL.h and SDL_syswm.h.
// Thus, we remove the exceptions for setjmp and longjmp before these two
// includes. Unfortunately, we can't use SDL_VERSION_ATLEAST here, as SDL.h
// hasn't been included yet at this point.
#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && defined(_MSC_VER)
// We unset any fake definitions of setjmp/longjmp here
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp
#undef setjmp
#endif
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_longjmp
#undef longjmp
#endif
#endif
#if defined(__SYMBIAN32__)
#include <esdl\SDL.h>
#else
#include <SDL.h>
#endif
#include <SDL_syswm.h>
// Restore the forbidden exceptions from the hack above
#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && defined(_MSC_VER)
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp
#undef setjmp
#define setjmp(a) FORBIDDEN_SYMBOL_REPLACEMENT
#endif
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_longjmp
#undef longjmp
#define longjmp(a,b) FORBIDDEN_SYMBOL_REPLACEMENT
#endif
#endif
// SDL_syswm.h will include windows.h on Win32. We need to undefine its
// ARRAYSIZE definition because we supply our own.
#undef ARRAYSIZE
#ifdef HACK_REDEFINE_ARRAYSIZE
#undef HACK_REDEFINE_ARRAYSIZE
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
#endif
// In a moment of brilliance Xlib.h included by SDL_syswm.h #defines the
// following names. In a moment of mental breakdown, which occured upon
// gazing at Xlib.h, LordHoto decided to undefine them to prevent havoc.
#ifdef Status
#undef Status
#endif
#ifdef Bool
#undef Bool
#endif
#ifdef True
#undef True
#endif
#ifdef False
#undef False
#endif
// Finally forbid FILE again (if it was forbidden to start with)
#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_FILE)
#undef FILE
@ -74,5 +146,50 @@ typedef struct { int FAKE; } FAKE_FILE;
#define strncasecmp FORBIDDEN_SYMBOL_REPLACEMENT
#endif
// SDL 2 has major API changes. We redefine constants which got renamed to
// ease the transition. This is sometimes dangerous because the values changed
// too!
#if SDL_VERSION_ATLEAST(2, 0, 0)
// Type names which changed between SDL 1.2 and SDL 2.
#define SDLKey SDL_Keycode
#define SDLMod SDL_Keymod
#define SDL_keysym SDL_Keysym
// Key code constants which got renamed.
#define SDLK_SCROLLOCK SDLK_SCROLLLOCK
#define SDLK_NUMLOCK SDLK_NUMLOCKCLEAR
#define SDLK_LSUPER SDLK_LGUI
#define SDLK_RSUPER SDLK_RGUI
#define SDLK_PRINT SDLK_PRINTSCREEN
#define SDLK_COMPOSE SDLK_APPLICATION
#define SDLK_KP0 SDLK_KP_0
#define SDLK_KP1 SDLK_KP_1
#define SDLK_KP2 SDLK_KP_2
#define SDLK_KP3 SDLK_KP_3
#define SDLK_KP4 SDLK_KP_4
#define SDLK_KP5 SDLK_KP_5
#define SDLK_KP6 SDLK_KP_6
#define SDLK_KP7 SDLK_KP_7
#define SDLK_KP8 SDLK_KP_8
#define SDLK_KP9 SDLK_KP_9
// Meta key constants which got renamed.
#define KMOD_META KMOD_GUI
// SDL surface flags which got removed.
#define SDL_SRCCOLORKEY 0
#define SDL_SRCALPHA 0
#define SDL_FULLSCREEN 0x40000000
// Compatibility implementations for removed functionality.
int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha);
#define SDL_SetColorKey SDL_SetColorKey_replacement
int SDL_SetColorKey_replacement(SDL_Surface *surface, Uint32 flag, Uint32 key);
#endif
#endif

View File

@ -0,0 +1,223 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/platform/sdl/sdl-window.h"
#include "common/textconsole.h"
#include "icons/scummvm.xpm"
SdlWindow::SdlWindow()
#if SDL_VERSION_ATLEAST(2, 0, 0)
: _window(nullptr), _inputGrabState(false), _windowCaption("ScummVM")
#endif
{
}
SdlWindow::~SdlWindow() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
destroyWindow();
#endif
}
void SdlWindow::setupIcon() {
int x, y, w, h, ncols, nbytes, i;
unsigned int rgba[256];
unsigned int *icon;
if (sscanf(scummvm_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes) != 4) {
warning("Wrong format of scummvm_icon[0] (%s)", scummvm_icon[0]);
return;
}
if ((w > 512) || (h > 512) || (ncols > 255) || (nbytes > 1)) {
warning("Could not load the built-in icon (%d %d %d %d)", w, h, ncols, nbytes);
return;
}
icon = (unsigned int*)malloc(w*h*sizeof(unsigned int));
if (!icon) {
warning("Could not allocate temp storage for the built-in icon");
return;
}
for (i = 0; i < ncols; i++) {
unsigned char code;
char color[32];
memset(color, 0, sizeof(color));
unsigned int col;
if (sscanf(scummvm_icon[1 + i], "%c c %s", &code, color) != 2) {
warning("Wrong format of scummvm_icon[%d] (%s)", 1 + i, scummvm_icon[1 + i]);
}
if (!strcmp(color, "None"))
col = 0x00000000;
else if (!strcmp(color, "black"))
col = 0xFF000000;
else if (color[0] == '#') {
if (sscanf(color + 1, "%06x", &col) != 1) {
warning("Wrong format of color (%s)", color + 1);
}
col |= 0xFF000000;
} else {
warning("Could not load the built-in icon (%d %s - %s) ", code, color, scummvm_icon[1 + i]);
free(icon);
return;
}
rgba[code] = col;
}
for (y = 0; y < h; y++) {
const char *line = scummvm_icon[1 + ncols + y];
for (x = 0; x < w; x++) {
icon[x + w * y] = rgba[(int)line[x]];
}
}
SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, w, h, 32, w * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
if (!sdl_surf) {
warning("SDL_CreateRGBSurfaceFrom(icon) failed");
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
SDL_SetWindowIcon(_window, sdl_surf);
}
#else
SDL_WM_SetIcon(sdl_surf, NULL);
#endif
SDL_FreeSurface(sdl_surf);
free(icon);
}
void SdlWindow::setWindowCaption(const Common::String &caption) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
_windowCaption = caption;
if (_window) {
SDL_SetWindowTitle(_window, caption.c_str());
}
#else
SDL_WM_SetCaption(caption.c_str(), caption.c_str());
#endif
}
void SdlWindow::toggleMouseGrab() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
_inputGrabState = !(SDL_GetWindowGrab(_window) == SDL_TRUE);
SDL_SetWindowGrab(_window, _inputGrabState ? SDL_TRUE : SDL_FALSE);
}
#else
if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
SDL_WM_GrabInput(SDL_GRAB_ON);
} else {
SDL_WM_GrabInput(SDL_GRAB_OFF);
}
#endif
}
bool SdlWindow::hasMouseFocus() const {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
return (SDL_GetWindowFlags(_window) & SDL_WINDOW_MOUSE_FOCUS);
} else {
return false;
}
#else
return (SDL_GetAppState() & SDL_APPMOUSEFOCUS);
#endif
}
void SdlWindow::warpMouseInWindow(uint x, uint y) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
SDL_WarpMouseInWindow(_window, x, y);
}
#else
SDL_WarpMouse(x, y);
#endif
}
void SdlWindow::iconifyWindow() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
SDL_MinimizeWindow(_window);
}
#else
SDL_WM_IconifyWindow();
#endif
}
bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const {
SDL_VERSION(&info->version);
#if SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_GetWindowWMInfo(_window, info);
#else
return SDL_GetWMInfo(info);
#endif
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Surface *copySDLSurface(SDL_Surface *src) {
const bool locked = SDL_MUSTLOCK(src) == SDL_TRUE;
if (locked) {
if (SDL_LockSurface(src) != 0) {
return nullptr;
}
}
SDL_Surface *res = SDL_CreateRGBSurfaceFrom(src->pixels,
src->w, src->h, src->format->BitsPerPixel,
src->pitch, src->format->Rmask, src->format->Gmask,
src->format->Bmask, src->format->Amask);
if (locked) {
SDL_UnlockSurface(src);
}
return res;
}
bool SdlWindow::createWindow(int width, int height, uint32 flags) {
destroyWindow();
if (_inputGrabState) {
flags |= SDL_WINDOW_INPUT_GRABBED;
}
_window = SDL_CreateWindow(_windowCaption.c_str(), SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, width, height, flags);
if (!_window) {
return false;
}
setupIcon();
return true;
}
void SdlWindow::destroyWindow() {
SDL_DestroyWindow(_window);
_window = nullptr;
}
#endif

View File

@ -0,0 +1,112 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_PLATFORM_SDL_WINDOW_H
#define BACKENDS_PLATFORM_SDL_WINDOW_H
#include "backends/platform/sdl/sdl-sys.h"
#include "common/str.h"
class SdlWindow {
public:
SdlWindow();
virtual ~SdlWindow();
/**
* Setup the window icon.
*/
virtual void setupIcon();
/**
* Change the caption of the window.
*
* @param caption New window caption in UTF-8 encoding.
*/
void setWindowCaption(const Common::String &caption);
/**
* Toggle mouse grab state. This decides whether the cursor can leave the
* window or not.
*/
void toggleMouseGrab();
/**
* Check whether the application has mouse focus.
*/
bool hasMouseFocus() const;
/**
* Warp the mouse to the specified position in window coordinates.
*/
void warpMouseInWindow(uint x, uint y);
/**
* Iconifies the window.
*/
void iconifyWindow();
/**
* Query platform specific SDL window manager information.
*
* Since this is an SDL internal structure clients are responsible
* for accessing it in a version safe manner.
*/
bool getSDLWMInformation(SDL_SysWMinfo *info) const;
#if SDL_VERSION_ATLEAST(2, 0, 0)
public:
/**
* @return The window ScummVM has setup with SDL.
*/
SDL_Window *getSDLWindow() const { return _window; }
/**
* Creates a new SDL window (and destroies the old one).
*
* @param width Width of the window.
* @param height Height of the window.
* @param flags SDL flags passed to SDL_CreateWindow
* @return true on success, false otherwise
*/
bool createWindow(int width, int height, uint32 flags);
/**
* Destroies the current SDL window.
*/
void destroyWindow();
protected:
SDL_Window *_window;
private:
bool _inputGrabState;
Common::String _windowCaption;
#endif
};
class SdlIconlessWindow : public SdlWindow {
public:
virtual void setupIcon() {}
};
#endif

Some files were not shown because too many files have changed in this diff Show More