Moved RetroFE to its own subdirectory.

This commit is contained in:
emb
2015-01-04 11:48:21 -06:00
parent 7001019513
commit 1458f13d9a
764 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,749 @@
<?xml version="1.0"?>
<menu>
<header>
<listname>MAME</listname>
<lastlistupdate>03/10/2012</lastlistupdate>
<listversion>.145 Ongoing</listversion>
<exporterversion>HyperList XML Exporter Version 1.3 Copywrite (c) 2009-2011 William Strong</exporterversion>
</header>
<game name="alphamis" index="" image="">
<description>Alpha Mission</description>
<cloneof>aso</cloneof>
<crc />
<manufacturer>SNK</manufacturer>
<year>1985</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="altbeast" index="" image="">
<description>Altered Beast (set 8, 8751 317-0078)</description>
<cloneof />
<crc />
<manufacturer>Sega</manufacturer>
<year>1988</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="arkanoid" index="" image="">
<description>Arkanoid (World)</description>
<cloneof />
<crc />
<manufacturer>Taito Corporation Japan</manufacturer>
<year>1986</year>
<genre>Breakout</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="btoads" index="" image="">
<description>Battle Toads</description>
<cloneof />
<crc />
<manufacturer>Rare</manufacturer>
<year>1994</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="bosco" index="" image="">
<description>Bosconian (new version)</description>
<cloneof />
<crc />
<manufacturer>Namco</manufacturer>
<year>1981</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="bublbobl" index="" image="">
<description>Bubble Bobble</description>
<cloneof />
<crc />
<manufacturer>Taito Corporation</manufacturer>
<year>1986</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="btime" index="" image="">
<description>Burger Time (Data East set 1)</description>
<cloneof />
<crc />
<manufacturer>Data East Corporation</manufacturer>
<year>1982</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="cabalus" index="" image="">
<description>Cabal (US set 1, Trackball version)</description>
<cloneof>cabal</cloneof>
<crc />
<manufacturer>TAD Corporation (Fabtek license)</manufacturer>
<year>1988</year>
<genre>Shooter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="dinou" index="" image="">
<description>Cadillacs and Dinosaurs (USA 930201)</description>
<cloneof>dino</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1993</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="centiped" index="" image="">
<description>Centipede (revision 3)</description>
<cloneof />
<crc />
<manufacturer>Atari</manufacturer>
<year>1980</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="darius" index="" image="">
<description>Darius (World)</description>
<cloneof />
<crc />
<manufacturer>Taito Corporation Japan</manufacturer>
<year>1986</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="digdug" index="" image="">
<description>Dig Dug (rev 2)</description>
<cloneof />
<crc />
<manufacturer>Namco</manufacturer>
<year>1982</year>
<genre>Maze</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="ddonpach" index="" image="">
<description>DoDonPachi (International, Master Ver. 97/02/05)</description>
<cloneof />
<crc />
<manufacturer>Cave (Atlus license)</manufacturer>
<year>1997</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="dkong" index="" image="">
<description>Donkey Kong (US set 1)</description>
<cloneof />
<crc />
<manufacturer>Nintendo of America</manufacturer>
<year>1981</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="galaga88" index="" image="">
<description>Galaga '88</description>
<cloneof />
<crc />
<manufacturer>Namco</manufacturer>
<year>1987</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="galaxian" index="" image="">
<description>Galaxian (Namco set 1)</description>
<cloneof />
<crc />
<manufacturer>Namco</manufacturer>
<year>1979</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="gngt" index="" image="">
<description>Ghosts'n Goblins (US)</description>
<cloneof>gng</cloneof>
<crc />
<manufacturer>Capcom (Taito America license)</manufacturer>
<year>1985</year>
<genre>Platform</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="ghoulsu" index="" image="">
<description>Ghouls'n Ghosts (USA)</description>
<cloneof>ghouls</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1988</year>
<genre>Platform</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="gt2k" index="" image="">
<description>Golden Tee 2K (v1.00)</description>
<cloneof />
<crc />
<manufacturer>Incredible Technologies</manufacturer>
<year>2000</year>
<genre>Sports/Golf</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="gradius" index="" image="">
<description>Gradius (Japan, ROM version)</description>
<cloneof>nemesis</cloneof>
<crc />
<manufacturer>Konami</manufacturer>
<year>1985</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="gyruss" index="" image="">
<description>Gyruss (Konami)</description>
<cloneof />
<crc />
<manufacturer>Konami</manufacturer>
<year>1983</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="hsf2" index="" image="">
<description>Hyper Street Fighter 2: The Anniversary Edition (USA 040202)</description>
<cloneof />
<crc />
<manufacturer>Capcom</manufacturer>
<year>2004</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="joust" index="" image="">
<description>Joust (White/Green label)</description>
<cloneof />
<crc />
<manufacturer>Williams</manufacturer>
<year>1982</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="jrpacman" index="" image="">
<description>Jr. Pac-Man</description>
<cloneof />
<crc />
<manufacturer>Bally Midway</manufacturer>
<year>1983</year>
<genre>Maze</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="kinst" index="" image="">
<description>Killer Instinct (v1.5d)</description>
<cloneof />
<crc />
<manufacturer>Rare</manufacturer>
<year>1994</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="kinst2" index="" image="">
<description>Killer Instinct 2 (v1.4)</description>
<cloneof />
<crc />
<manufacturer>Rare</manufacturer>
<year>1995</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="marble" index="" image="">
<description>Marble Madness (set 1)</description>
<cloneof />
<crc />
<manufacturer>Atari Games</manufacturer>
<year>1984</year>
<genre>Maze</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="mario" index="" image="">
<description>Mario Bros. (US, Revision F)</description>
<cloneof />
<crc />
<manufacturer>Nintendo of America</manufacturer>
<year>1983</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="mshu" index="" image="">
<description>Marvel Super Heroes (USA 951024)</description>
<cloneof>msh</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1995</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="mshvsfu" index="" image="">
<description>Marvel Super Heroes Vs. Street Fighter (USA 970827)</description>
<cloneof>mshvsf</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1997</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="mvscu" index="" image="">
<description>Marvel Vs. Capcom: Clash of Super Heroes (USA 980123)</description>
<cloneof>mvsc</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1998</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="mslug" index="" image="">
<description>Metal Slug - Super Vehicle-001</description>
<cloneof />
<crc />
<manufacturer>Nazca</manufacturer>
<year>1996</year>
<genre>Platform</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mslug2" index="" image="">
<description>Metal Slug 2 - Super Vehicle-001/II</description>
<cloneof />
<crc />
<manufacturer>SNK</manufacturer>
<year>1998</year>
<genre>Platform</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mslug3" index="" image="">
<description>Metal Slug 3 (NGM-2560)</description>
<cloneof />
<crc />
<manufacturer>SNK</manufacturer>
<year>2000</year>
<genre>Platform</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mslug4" index="" image="">
<description>Metal Slug 4 (NGM-2630)</description>
<cloneof />
<crc />
<manufacturer>Mega / Playmore</manufacturer>
<year>2002</year>
<genre>Platform</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mslug5" index="" image="">
<description>Metal Slug 5 (NGM-2680)</description>
<cloneof />
<crc />
<manufacturer>SNK Playmore</manufacturer>
<year>2003</year>
<genre>Platform</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mslugx" index="" image="">
<description>Metal Slug X - Super Vehicle-001</description>
<cloneof />
<crc />
<manufacturer>SNK</manufacturer>
<year>1999</year>
<genre>Platform</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mwalku" index="" image="">
<description>Michael Jackson's Moonwalker (US, FD1094/8751 317-0158)</description>
<cloneof>mwalk</cloneof>
<crc />
<manufacturer>Sega</manufacturer>
<year>1990</year>
<genre>Maze</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="milliped" index="" image="">
<description>Millipede</description>
<cloneof />
<crc />
<manufacturer>Atari</manufacturer>
<year>1982</year>
<genre>Shoot-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="missile" index="" image="">
<description>Missile Command (rev 3)</description>
<cloneof />
<crc />
<manufacturer>Atari</manufacturer>
<year>1980</year>
<genre>Shooter</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="mk" index="" image="">
<description>Mortal Kombat (rev 5.0 T-Unit 03/19/93)</description>
<cloneof />
<crc />
<manufacturer>Midway</manufacturer>
<year>1992</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Life Like Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mk3" index="" image="">
<description>Mortal Kombat 3 (rev 2.1)</description>
<cloneof />
<crc />
<manufacturer>Midway</manufacturer>
<year>1994</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Life Like Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mk2" index="" image="">
<description>Mortal Kombat II (rev L3.1)</description>
<cloneof />
<crc />
<manufacturer>Midway</manufacturer>
<year>1993</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Life Like Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="mrdrillr" index="" image="">
<description>Mr. Driller (Japan, DRI1/VER.A2)</description>
<cloneof />
<crc />
<manufacturer>Namco</manufacturer>
<year>1999</year>
<genre>Puzzle</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="mspacman" index="" image="">
<description>Ms. Pac-Man</description>
<cloneof />
<crc />
<manufacturer>Midway</manufacturer>
<year>1981</year>
<genre>Maze</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="nbajamte" index="" image="">
<description>NBA Jam TE (rev 4.0 03/23/94)</description>
<cloneof />
<crc />
<manufacturer>Midway</manufacturer>
<year>1994</year>
<genre>Sports/Basketball</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="neobombe" index="" image="">
<description>Neo Bomberman</description>
<cloneof />
<crc />
<manufacturer>Hudson</manufacturer>
<year>1997</year>
<genre>Maze</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="25pacman" index="" image="">
<description>Pac-Man - 25th Anniversary Edition (Rev 2.00)</description>
<cloneof />
<crc />
<manufacturer>Namco</manufacturer>
<year>2005</year>
<genre>Maze</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="puzzloopu" index="" image="">
<description>Puzz Loop (USA)</description>
<cloneof>puzzloop</cloneof>
<crc />
<manufacturer>Mitchell</manufacturer>
<year>1998</year>
<genre>Puzzle</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="pbobble" index="" image="">
<description>Puzzle Bobble (Japan, B-System)</description>
<cloneof />
<crc />
<manufacturer>Taito Corporation</manufacturer>
<year>1994</year>
<genre>Puzzle</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="rampage" index="" image="">
<description>Rampage (ver 3 8-27-86)</description>
<cloneof />
<crc />
<manufacturer>Bally Midway</manufacturer>
<year>1986</year>
<genre>Action</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="rampart" index="" image="">
<description>Rampart (Trackball)</description>
<cloneof />
<crc />
<manufacturer>Atari Games</manufacturer>
<year>1990</year>
<genre>Shooter</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="rygar" index="" image="">
<description>Rygar (US set 1)</description>
<cloneof />
<crc />
<manufacturer>Tecmo</manufacturer>
<year>1986</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="shufshot" index="" image="">
<description>Shuffleshot (v1.40)</description>
<cloneof />
<crc />
<manufacturer>Strata/Incredible Technologies</manufacturer>
<year>1997</year>
<genre>Sports</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="simpbowl" index="" image="">
<description>Simpsons Bowling (GQ829 UAA)</description>
<cloneof />
<crc />
<manufacturer>Konami</manufacturer>
<year>2000</year>
<genre>Sports/Bowling</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="sfa3u" index="" image="">
<description>Street Fighter Alpha 3 (USA 980904)</description>
<cloneof>sfa3</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1998</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="sf2ceua" index="" image="">
<description>Street Fighter II': Champion Edition (USA 920313)</description>
<cloneof>sf2ce</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1992</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="sfiii3u" index="" image="">
<description>Street Fighter III 3rd Strike: Fight for the Future (USA 990608)</description>
<cloneof>sfiii3</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1999</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="strider" index="" image="">
<description>Strider (USA, B-Board 89624B-2)</description>
<cloneof />
<crc />
<manufacturer>Capcom</manufacturer>
<year>1989</year>
<genre>Platform</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="ssridersubc" index="" image="">
<description>Sunset Riders (2 Players ver UBC)</description>
<cloneof>ssriders</cloneof>
<crc />
<manufacturer>Konami</manufacturer>
<year>1991</year>
<genre>Platform</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="sgemf" index="" image="">
<description>Super Gem Fighter Mini Mix (USA 970904)</description>
<cloneof />
<crc />
<manufacturer>Capcom</manufacturer>
<year>1997</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="spf2t" index="" image="">
<description>Super Puzzle Fighter II Turbo (USA 960620)</description>
<cloneof />
<crc />
<manufacturer>Capcom</manufacturer>
<year>1996</year>
<genre>Puzzle</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="tapper" index="" image="">
<description>Tapper (Budweiser, set 1)</description>
<cloneof />
<crc />
<manufacturer>Bally Midway</manufacturer>
<year>1983</year>
<genre>Miscellaneous</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="tmnt2pj" index="" image="">
<description>Teenage Mutant Ninja Turtles (Japan 2 Players)</description>
<cloneof>tmnt</cloneof>
<crc />
<manufacturer>Konami</manufacturer>
<year>1990</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="tmnt22pu" index="" image="">
<description>Teenage Mutant Ninja Turtles - Turtles in Time (2 Players ver UDA)</description>
<cloneof>tmnt2</cloneof>
<crc />
<manufacturer>Konami</manufacturer>
<year>1991</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="tgm2" index="" image="">
<description>Tetris the Absolute The Grand Master 2</description>
<cloneof />
<crc />
<manufacturer>Arika</manufacturer>
<year>2000</year>
<genre>Puzzle</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="kof2000" index="" image="">
<description>The King of Fighters 2000 (NGM-2570) (NGH-2570)</description>
<cloneof />
<crc />
<manufacturer>SNK</manufacturer>
<year>2000</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="simpsons2p" index="" image="">
<description>The Simpsons (2 Players World, set 1)</description>
<cloneof>simpsons</cloneof>
<crc />
<manufacturer>Konami</manufacturer>
<year>1991</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="tron" index="" image="">
<description>Tron (8/9)</description>
<cloneof />
<crc />
<manufacturer>Bally Midway</manufacturer>
<year>1982</year>
<genre>Compilation</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="umk3" index="" image="">
<description>Ultimate Mortal Kombat 3 (rev 1.2)</description>
<cloneof />
<crc />
<manufacturer>Midway</manufacturer>
<year>1994</year>
<genre>Fighter</genre>
<rating>AAMA - Red (Animated Violence Strong)</rating>
<enabled>Yes</enabled>
</game>
<game name="wcbowldx" index="" image="">
<description>World Class Bowling Deluxe (v2.00)</description>
<cloneof />
<crc />
<manufacturer>Incredible Technologies</manufacturer>
<year>1999</year>
<genre>Sports/Bowling</genre>
<rating>AAMA - Green (Suitable For All Ages)</rating>
<enabled>Yes</enabled>
</game>
<game name="xmen2pe" index="" image="">
<description>X-Men (2 Players ver EAA)</description>
<cloneof>xmen</cloneof>
<crc />
<manufacturer>Konami</manufacturer>
<year>1992</year>
<genre>Beat-'Em-Up</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="xmvsfu" index="" image="">
<description>X-Men Vs. Street Fighter (USA 961023)</description>
<cloneof>xmvsf</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1996</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
<game name="xmcotau" index="" image="">
<description>X-Men: Children of the Atom (USA 950105)</description>
<cloneof>xmcota</cloneof>
<crc />
<manufacturer>Capcom</manufacturer>
<year>1994</year>
<genre>Fighter</genre>
<rating>AAMA - Yellow (Animated Violence Mild)</rating>
<enabled>Yes</enabled>
</game>
</menu>

View File

@@ -0,0 +1,13 @@
# override the default layout with something else if desired
#layout = Default
launcher = MAME
list.path = ../ROMs/MAME
list.extensions = zip
media.video = ../Media/Arcade/Videos
media.title = ../Media/Arcade/Titles
media.snap = ../Media/Arcade/Snaps
media.box = ../Media/Arcade/Boxes
media.cart = ../Media/Arcade/Carts

View File

@@ -0,0 +1,7 @@
<menu>
<!-- This is the main menu. Mark which collections you would like to make visible -->
<item collection="Nintendo Entertainment System"/>
<item collection="NES and Arcade"/>
<item collection="Arcade"/>
<item collection="Main"/>
</menu>

View File

@@ -0,0 +1,9 @@
# override the default layout with something else if desired
# layout = SomeCustomBuiltTheme
media.video = ../Media/Systems/Videos
media.title = ../Media/Systems/Titles
media.box = ../Media/Systems/Boxes
media.cart = ../Media/Systems/Carts
media.snap = ../Media/Systems/Snaps

View File

@@ -0,0 +1,5 @@
<menu>
<!-- This is the main menu. Mark which collections you would like to make visible -->
<item collection="Arcade" import="true"/>
<item collection="Nintendo Entertainment System" import="true"/>
</menu>

View File

@@ -0,0 +1,9 @@
# override the default layout with something else if desired
# layout = SomeCustomBuiltTheme
media.video = ../Media/Systems/Videos
media.title = ../Media/Systems/Titles
media.box = ../Media/Systems/Boxes
media.cart = ../Media/Systems/Carts
media.snap = ../Media/Systems/Snaps

View File

@@ -0,0 +1,13 @@
# override the default layout with something else if desired
#layout = SomeNewLayout
launcher = Nestopia
list.path = ../ROMs/Nintendo NES
list.extensions = nes
media.video = ../Media/Nintendo NES/Videos
media.title = ../Media/Nintendo NES/Titles
media.box = ../Media/Nintendo NES/Boxes
media.cart = ../Media/Nintendo NES/Carts
media.snap = ../Media/Nintendo NES/Snaps

View File

@@ -0,0 +1,7 @@
previousItem = Up
nextItem = Down
pageUp = Left
pageDown = Right
select = Space
back = Escape
quit = Q

View File

@@ -0,0 +1,2 @@
executable = D:\Emulators\fceux\fceux.exe
arguments = "%ITEM_FILEPATH%"

View File

@@ -0,0 +1,6 @@
executable = ../HyperLaunch/HyperLaunch.exe
arguments = -s "%ITEM_COLLECTION_NAME%" -r "%ITEM_FILEPATH%" -p RetroFE -f "%RETROFE_EXEC_PATH%"
# For v3.0.1.1 compliant version, comment out the above arguments
# and uncomment the following line:
#arguments = "%ITEM_COLLECTION_NAME%" "%ITEM_NAME%"

View File

@@ -0,0 +1,2 @@
executable = D:/Emulators/MAME/mamepp.exe
arguments = "%ITEM_NAME%"

View File

@@ -0,0 +1,2 @@
executable = D:\Emulators\nestopia\nestopia.exe
arguments = "%ITEM_FILEPATH%"

View File

@@ -0,0 +1,79 @@
<!-- todo: add collection name text tag, metadata tags -->
<!-- todo: collection name text tag will load image, or text if image does not exist -->
<layout width="1920" height="1080" font="Age.otf" fontColor="ecf0f1">
<sound type="load" src="load.wav" />
<sound type="unload" src="unload.wav" />
<sound type="highlight" src="highlight.wav" />
<sound type="select" src="select.wav" />
<!-- border for game name (in menu) -->
<container backgroundColor="222222" backgroundAlpha="99" x="0" y="bottom" yOrigin="center" yOffset="-175" height="150" width="stretch" layer="1" alpha="0.3" />
<reloadableText type="title" x="0" y="bottom" xOffset="20" yOrigin="center" yOffset="-175" height="150" width="stretch" layer="1" fontSize="50">
<onHighlightEnter>
<set duration=".2">
<animate type="x" from="right" to="0" algorithm="easeinquadratic"/>
<animate type="xOrigin" from="right" to="left" algorithm="easeinquadratic"/>
</set>
</onHighlightEnter>
<onHighlightExit>
<set duration=".2">
<animate type="x" from="0" to="left" algorithm="easeinquadratic"/>
<animate type="xOrigin" from="left" to="right" algorithm="easeinquadratic"/>
</set>
</onHighlightExit>
</reloadableText>
<!--
<reloadableText type="manufacturer" x="left" y="bottom" xOrigin="left" yOrigin="bottom" xOffset="10" yOffset="-10" fontSize="20" layer="0">
</reloadableText>
<reloadableText type="year" x="left" y="bottom" xOrigin="left" yOrigin="bottom" xOffset="10" yOffset="-30" fontSize="20" layer="0">
</reloadableText>
<reloadableText type="numberPlayers" x="left" y="bottom" xOrigin="left" yOrigin="bottom" xOffset="10" yOffset="-70" fontSize="20" layer="0">
</reloadableText>
<reloadableText type="numberButtons" x="left" y="bottom" xOrigin="left" yOrigin="bottom" xOffset="10" yOffset="-90" fontSize="20" layer="0">
</reloadableText>
-->
<menu orientation="horizontal" imageType="title" algorithm="easeincircular" x="100" y="100" height="700" speed="0.05" acceleration="0.05">
<itemDefaults height="50" spacing="10" alpha="0.5" xOffset="20" yOrigin="center" />
<item index="start" height="0" spacing="0" alpha="0"/>
<item index="2" spacing="0" height="100" fontSize="100" alpha="1" selected="true"/>
<item index="end" height="0" spacing="0" alpha="0"/>
</menu>
<image x="30" y="20" height="80" src="logo.png" layer="1" alpha="0.3">
<onEnter>
<set duration=".5">
<animate type="y" from="top" to="20" algorithm="easeinquadratic"/>
<animate type="yOrigin" from="bottom" to="top" algorithm="easeincircular"/>
</set>
</onEnter>
<onExit>
<set duration=".25">
<animate type="y" from="20" to="top" algorithm="easeinquadratic"/>
<animate type="yOrigin" from="top" to="bottom" algorithm="easeincircular"/>
</set>
</onExit>
</image>
<reloadableVideo imageType="snap" x="0" y="0" width="stretch" height="stretch" fontSize="20" layer="0">
<onHighlightEnter>
<set duration=".4">
<animate type="alpha" from="0" to="1" algorithm="easeinquadratic"/>
</set>
</onHighlightEnter>
<onHighlightExit>
<set duration=".2">
<animate type="alpha" from="1" to="0" algorithm="easeinquadratic"/>
</set>
</onHighlightExit>
</reloadableVideo>
</layout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

View File

@@ -0,0 +1,49 @@
##############################################################################
#
# For support, visit www.retrofe.com
# or visit irc.foreverchat.net (channel #retrofe)
#
# For Documentation, visit www.retrofe.com/documentation
#
##############################################################################
#######################################
# Display
#######################################
fullscreen = no
horizontal = stretch # or enter in the screen pixel width (i.e 1024)
vertical = stretch # or enter in the screen pixel width (i.e 768)
layout = Default 16x9
hideMouse = yes
showParenthesis = yes
showSquareBrackets = yes
# If you would like RetroFE first load a different collection other than the
# main menu, uncomment the line below and replace it with your collection name.
# firstCollection = Arcade
#######################################
# Video playback settings
#######################################
# set to "no" for very old/slow systems
videoEnable = yes
# Number of times to loop video playback (enter 0 to continuously loop)
videoLoop = 0
#######################################
# General
#######################################
# exit the frontend when the back button is pressed on the first page
exitOnFirstPageBack = yes
# enter 0 attract mode, otherwise enter the number of seconds to wait before enabling attract mode
attractModeTime = 45
#######################################
# Debugging
#######################################
# do not set to "yes" unless you want your hard drive to quickly fill up
debug.logfps = no

View File

@@ -0,0 +1,4 @@
This program requires Ubuntu 14.04 and requires the following packages to be installed:
sudo apt-get install zlib1g libsdl2-2.0 libsdl2-mixer-2.0 libsdl2-image-2.0 libsdl2-ttf-2.0 \
libgstreamer1.0 libgstreamer-plugins-base1.0 libgstreamer-plugins-good1.0 libglib2.0

Binary file not shown.

View File

@@ -0,0 +1,49 @@
FILE(TO_CMAKE_PATH "$ENV{GSTREAMER_ROOT}" TRY1_DIR)
FILE(TO_CMAKE_PATH "${GSTREAMER_ROOT}" TRY2_DIR)
FILE(GLOB GSTREAMER_ROOT ${TRY1_DIR} ${TRY2_DIR})
FIND_PATH(GSTREAMER_gst_INCLUDE_DIR gst/gst.h
PATHS ${GSTREAMER_ROOT}/include ${GSTREAMER_ROOT}/include/gstreamer-1.0 /usr/local/include/gstreamer-1.0 /usr/include/gstreamer-1.0
ENV INCLUDE DOC "Directory containing gst/gst.h include file")
FIND_PATH(GSTREAMER_gstconfig_INCLUDE_DIR gst/gstconfig.h
PATHS ${GSTREAMER_ROOT}/include ${GSTREAMER_ROOT}/lib/include ${GSTREAMER_ROOT}/include/gstreamer-1.0 /usr/local/include/gstreamer-1.0 /usr/include/gstreamer-1.0 /usr/local/lib/include/gstreamer-1.0 /usr/lib/include/gstreamer-1.0
ENV INCLUDE DOC "Directory containing gst/gstconfig.h include file")
FIND_LIBRARY(GSTREAMER_gstaudio_LIBRARY NAMES gstaudio-1.0 libgstaudio-1.0 gstaudio
PATHS ${GSTREAMER_ROOT}/bin ${GSTREAMER_ROOT}/win32/bin ${GSTREAMER_ROOT}/bin/bin C:/gstreamer/bin ${GSTREAMER_ROOT}/lib ${GSTREAMER_ROOT}/win32/lib /usr/local/lib /usr/lib
ENV LIB
DOC "gstaudio library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
FIND_LIBRARY(GSTREAMER_gstreamer_LIBRARY NAMES gstreamer-1.0 libgstreamer-1.0 gstreamer
PATHS ${GSTREAMER_ROOT}/bin ${GSTREAMER_ROOT}/win32/bin ${GSTREAMER_ROOT}/bin/bin C:/gstreamer/bin ${GSTREAMER_ROOT}/lib ${GSTREAMER_ROOT}/win32/lib /usr/local/lib /usr/lib
ENV LIB
DOC "gstreamer library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
FIND_LIBRARY(GSTREAMER_gstbase_LIBRARY NAMES gstbase-1.0 libgstbase-1.0 gstbase
PATHS ${GSTREAMER_ROOT}/bin ${GSTREAMER_ROOT}/win32/bin ${GSTREAMER_ROOT}/bin/bin C:/gstreamer/bin ${GSTREAMER_ROOT}/lib ${GSTREAMER_ROOT}/win32/lib /usr/local/lib /usr/lib
ENV LIB
DOC "gstbase library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
FIND_LIBRARY(GSTREAMER_gstvideo_LIBRARY NAMES gstvideo-1.0 libgstvideo-1.0 gstvideo
PATHS ${GSTREAMER_ROOT}/bin ${GSTREAMER_ROOT}/win32/bin ${GSTREAMER_ROOT}/bin/bin C:/gstreamer/bin ${GSTREAMER_ROOT}/lib ${GSTREAMER_ROOT}/win32/lib /usr/local/lib /usr/lib
ENV LIB
DOC "gstvideo library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
#IF (GSTREAMER_gst_INCLUDE_DIR AND GSTREAMER_gstconfig_INCLUDE_DIR AND
# GSTREAMER_gstaudio_LIBRARY AND GSTREAMER_gstbase_LIBRARY AND
# GSTREAMER_gstreamer_LIBRARY AND GSTREAMER_gstvideo_LIBRARY)
SET(GSTREAMER_INCLUDE_DIRS ${GSTREAMER_gst_INCLUDE_DIR} ${GSTREAMER_gstconfig_INCLUDE_DIR})
list(REMOVE_DUPLICATES GSTREAMER_INCLUDE_DIRS)
SET(GSTREAMER_LIBRARIES ${GSTREAMER_gstaudio_LIBRARY} ${GSTREAMER_gstbase_LIBRARY}
${GSTREAMER_gstreamer_LIBRARY} ${GSTREAMER_gstvideo_LIBRARY})
list(REMOVE_DUPLICATES GSTREAMER_LIBRARIES)
SET(GSTREAMER_FOUND TRUE)
#ENDIF (GSTREAMER_gst_INCLUDE_DIR AND GSTREAMER_gstconfig_INCLUDE_DIR AND
# GSTREAMER_gstaudio_LIBRARY AND GSTREAMER_gstbase_LIBRARY AND
# GSTREAMER_gstreamer_LIBRARY AND GSTREAMER_gstvideo_LIBRARY)

View File

@@ -0,0 +1,43 @@
FILE(TO_CMAKE_PATH "$ENV{GLIB2_ROOT}" TRY1_DIR)
FILE(TO_CMAKE_PATH "${GLIB2_ROOT}" TRY2_DIR)
FILE(GLOB GLIB2_ROOT ${TRY1_DIR} ${TRY2_DIR})
FIND_PATH(GLIB_glib_2_INCLUDE_DIR glib.h
PATHS ${GLIB2_ROOT}/include ${GLIB2_ROOT}/include/glib-2.0 /usr/local/include/glib-2.0 /usr/include/glib-2.0 /opt/local/include/glib-2.0
ENV INCLUDE DOC "Directory containing glib.h include file")
FIND_PATH(GLIB_glibconfig_2_INCLUDE_DIR glibconfig.h
PATHS ${GLIB2_ROOT}/include ${GLIB2_ROOT}/include/glib-2.0 ${GLIB2_ROOT}/lib/include ${GLIB2_ROOT}/lib/glib-2.0/include /usr/local/include/glib-2.0 /usr/include/glib-2.0 /usr/lib/glib-2.0/include /usr/local/lib/glib-2.0/include /opt/local/lib/glib-2.0/include
ENV INCLUDE DOC "Directory containing glibconfig.h include file")
FIND_LIBRARY(GLIB_glib_2_LIBRARY NAMES glib-2.0
PATHS ${GLIB2_ROOT}/bin ${GLIB2_ROOT}/win32/bin ${GLIB2_ROOT}/lib ${GLIB2_ROOT}/win32/lib /usr/local/lib /usr/lib /opt/local/lib
ENV LIB
DOC "glib library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
FIND_LIBRARY(GLIB_gmodule_2_LIBRARY NAMES gmodule-2.0
PATHS ${GLIB2_ROOT}/bin ${GLIB2_ROOT}/win32/bin ${GLIB2_ROOT}/lib ${GLIB2_ROOT}/win32/lib /usr/local/lib /usr/lib /opt/local/lib
ENV LIB
DOC "gmodule library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
FIND_LIBRARY(GLIB_gobject_2_LIBRARY NAMES gobject-2.0
PATHS ${GLIB2_ROOT}/bin ${GLIB2_ROOT}/win32/bin ${GLIB2_ROOT}/lib ${GLIB2_ROOT}/win32/lib /usr/local/lib /usr/lib /opt/local/lib
ENV LIB
DOC "gobject library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
FIND_LIBRARY(GLIB_gthread_2_LIBRARY NAMES gthread-2.0
PATHS ${GLIB2_ROOT}/bin ${GLIB2_ROOT}/win32/bin ${GLIB2_ROOT}/lib ${GLIB2_ROOT}/win32/lib /usr/local/lib /usr/lib /opt/local/lib
ENV LIB
DOC "gthread library to link with"
NO_SYSTEM_ENVIRONMENT_PATH)
#IF (GLIB_glib_2_INCLUDE_DIR AND GLIB_glibconfig_2_INCLUDE_DIR AND GLIB_glib_2_LIBRARY AND GLIB_gmodule_2_LIBRARY AND GLIB_gobject_2_LIBRARY AND GLIB_gthread_2_LIBRARY)
SET(GLIB2_INCLUDE_DIRS ${GLIB_glib_2_INCLUDE_DIR} ${GLIB_glib_2_INCLUDE_DIR}/glib-2.0 ${GLIB_glibconfig_2_INCLUDE_DIR})
list(REMOVE_DUPLICATES GLIB2_INCLUDE_DIRS)
SET(GLIB2_LIBRARIES ${GLIB_glib_2_LIBRARY} ${GLIB_gmodule_2_LIBRARY} ${GLIB_gobject_2_LIBRARY} ${GLIB_gthread_2_LIBRARY})
list(REMOVE_DUPLICATES GLIB2_LIBRARIES)
SET(GLIB2_FOUND TRUE)
#ENDIF (GLIB_glib_2_INCLUDE_DIR AND GLIB_glibconfig_2_INCLUDE_DIR AND GLIB_glib_2_LIBRARY AND GLIB_gmodule_2_LIBRARY AND GLIB_gobject_2_LIBRARY AND GLIB_gthread_2_LIBRARY)

View File

@@ -0,0 +1,87 @@
# - Find SDL2 library and headers
#
# Find module for SDL 2.0 (http://www.libsdl.org/).
# It defines the following variables:
# SDL2_INCLUDE_DIRS - The location of the headers, e.g., SDL.h.
# SDL2_LIBRARIES - The libraries to link against to use SDL2.
# SDL2_FOUND - If false, do not try to use SDL2.
# SDL2_VERSION_STRING - Human-readable string containing the version of SDL2.
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARIES variable.
#
# Also defined, but not for general use are:
# SDL2_INCLUDE_DIR - The directory that contains SDL.h.
# SDL2_LIBRARY - The location of the SDL2 library.
# SDL2MAIN_LIBRARY - The location of the SDL2main library.
#
#=============================================================================
# Copyright 2013 Benjamin Eikel
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_SDL2 QUIET sdl2)
find_path(SDL2_INCLUDE_DIR
NAMES SDL2/SDL.h
HINTS
${SDL2_ROOT}/include
)
find_library(SDL2_LIBRARY
NAMES SDL2
HINTS
${SDL2_ROOT}/lib
PATH_SUFFIXES x86
)
if(NOT SDL2_BUILDING_LIBRARY)
find_library(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
${SDL2_ROOT}/lib
PATH_SUFFIXES x86
)
endif()
if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL2/SDL_version.h")
file(STRINGS "${SDL2_INCLUDE_DIR}/SDL2/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_INCLUDE_DIR}/SDL2/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_INCLUDE_DIR}/SDL2/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$")
string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}")
set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH})
unset(SDL2_VERSION_MAJOR_LINE)
unset(SDL2_VERSION_MINOR_LINE)
unset(SDL2_VERSION_PATCH_LINE)
unset(SDL2_VERSION_MAJOR)
unset(SDL2_VERSION_MINOR)
unset(SDL2_VERSION_PATCH)
endif()
set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR} ${SDL2_INCLUDE_DIR}/SDL2)
set(SDL2_LIBRARIES ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL2
REQUIRED_VARS SDL2_INCLUDE_DIR SDL2_LIBRARY
VERSION_VAR SDL2_VERSION_STRING)
mark_as_advanced(SDL2_INCLUDE_DIR SDL2_LIBRARY)

View File

@@ -0,0 +1,70 @@
# - Find SDL2_image library and headers
#
# Find module for SDL_image 2.0 (http://www.libsdl.org/projects/SDL_image/).
# It defines the following variables:
# SDL2_IMAGE_INCLUDE_DIRS - The location of the headers, e.g., SDL_image.h.
# SDL2_IMAGE_LIBRARIES - The libraries to link against to use SDL2_image.
# SDL2_IMAGE_FOUND - If false, do not try to use SDL2_image.
# SDL2_IMAGE_VERSION_STRING
# Human-readable string containing the version of SDL2_image.
#
# Also defined, but not for general use are:
# SDL2_IMAGE_INCLUDE_DIR - The directory that contains SDL_image.h.
# SDL2_IMAGE_LIBRARY - The location of the SDL2_image library.
#
#=============================================================================
# Copyright 2013 Benjamin Eikel
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_SDL2_IMAGE QUIET SDL2_image)
find_path(SDL2_IMAGE_INCLUDE_DIR
NAMES SDL2/SDL_image.h
HINTS
${SDL2_IMAGE_ROOT}/include
)
find_library(SDL2_IMAGE_LIBRARY
NAMES SDL2_image
HINTS
${SDL2_IMAGE_ROOT}/lib
PATH_SUFFIXES x86
)
if(SDL2_IMAGE_INCLUDE_DIR AND EXISTS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2/SDL_image.h")
file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2/SDL_image.h" SDL2_IMAGE_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_IMAGE_MAJOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2/SDL_image.h" SDL2_IMAGE_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_IMAGE_MINOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2/SDL_image.h" SDL2_IMAGE_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_IMAGE_PATCHLEVEL[ \t]+[0-9]+$")
string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MAJOR "${SDL2_IMAGE_VERSION_MAJOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MINOR "${SDL2_IMAGE_VERSION_MINOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_IMAGE_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_PATCH "${SDL2_IMAGE_VERSION_PATCH_LINE}")
set(SDL2_IMAGE_VERSION_STRING ${SDL2_IMAGE_VERSION_MAJOR}.${SDL2_IMAGE_VERSION_MINOR}.${SDL2_IMAGE_VERSION_PATCH})
unset(SDL2_IMAGE_VERSION_MAJOR_LINE)
unset(SDL2_IMAGE_VERSION_MINOR_LINE)
unset(SDL2_IMAGE_VERSION_PATCH_LINE)
unset(SDL2_IMAGE_VERSION_MAJOR)
unset(SDL2_IMAGE_VERSION_MINOR)
unset(SDL2_IMAGE_VERSION_PATCH)
endif()
set(SDL2_IMAGE_INCLUDE_DIRS ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR}/SDL2)
set(SDL2_IMAGE_LIBRARIES ${SDL2_IMAGE_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL2_image
REQUIRED_VARS SDL2_IMAGE_INCLUDE_DIRS SDL2_IMAGE_LIBRARIES
VERSION_VAR SDL2_IMAGE_VERSION_STRING)
mark_as_advanced(SDL2_IMAGE_INCLUDE_DIR SDL2_IMAGE_LIBRARY)

View File

@@ -0,0 +1,72 @@
# Downloaded from http://cmake.3232098.n2.nabble.com/Find-modules-for-SDL2-td7585211.html and adapted to SDL2_mixer
#
# - Find SDL2_mixer library and headers
#
# Find module for SDL_mixer 2.0 (http://www.libsdl.org/projects/SDL_mixer/).
# It defines the following variables:
# SDL2_MIXER_INCLUDE_DIRS - The location of the headers, e.g., SDL_mixer.h.
# SDL2_MIXER_LIBRARIES - The libraries to link against to use SDL2_mixer.
# SDL2_MIXER_FOUND - If false, do not try to use SDL2_mixer.
# SDL2_MIXER_VERSION_STRING
# Human-readable string containing the version of SDL2_mixer.
#
# Also defined, but not for general use are:
# SDL2_MIXER_INCLUDE_DIR - The directory that contains SDL_mixer.h.
# SDL2_MIXER_LIBRARY - The location of the SDL2_mixer library.
#
#=============================================================================
# Copyright 2013 Benjamin Eikel
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_SDL2_MIXER QUIET SDL2_mixer)
find_path(SDL2_MIXER_INCLUDE_DIR
NAMES SDL2/SDL_mixer.h
HINTS
${SDL2_MIXER_ROOT}/include
)
find_library(SDL2_MIXER_LIBRARY
NAMES SDL2_mixer
HINTS
${SDL2_MIXER_ROOT}/lib
PATH_SUFFIXES x86
)
if(SDL2_MIXER_INCLUDE_DIR AND EXISTS "${SDL2_MIXER_INCLUDE_DIR}/SDL2/SDL_mixer.h")
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL2/SDL_mixer.h" SDL2_MIXER_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL2/SDL_mixer.h" SDL2_MIXER_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL2/SDL_mixer.h" SDL2_MIXER_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+[0-9]+$")
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MAJOR "${SDL2_MIXER_VERSION_MAJOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MINOR "${SDL2_MIXER_VERSION_MINOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_PATCH "${SDL2_MIXER_VERSION_PATCH_LINE}")
set(SDL2_MIXER_VERSION_STRING ${SDL2_MIXER_VERSION_MAJOR}.${SDL2_MIXER_VERSION_MINOR}.${SDL2_MIXER_VERSION_PATCH})
unset(SDL2_MIXER_VERSION_MAJOR_LINE)
unset(SDL2_MIXER_VERSION_MINOR_LINE)
unset(SDL2_MIXER_VERSION_PATCH_LINE)
unset(SDL2_MIXER_VERSION_MAJOR)
unset(SDL2_MIXER_VERSION_MINOR)
unset(SDL2_MIXER_VERSION_PATCH)
endif()
set(SDL2_MIXER_INCLUDE_DIRS ${SDL2_MIXER_INCLUDE_DIR} ${SDL2_MIXER_INCLUDE_DIR}/SDL2)
set(SDL2_MIXER_LIBRARIES ${SDL2_MIXER_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL2_mixer
REQUIRED_VARS SDL2_MIXER_INCLUDE_DIRS SDL2_MIXER_LIBRARIES
VERSION_VAR SDL2_MIXER_VERSION_STRING)
mark_as_advanced(SDL2_MIXER_INCLUDE_DIR SDL2_MIXER_LIBRARY)

View File

@@ -0,0 +1,73 @@
# Downloaded from http://cmake.3232098.n2.nabble.com/Find-modules-for-SDL2-td7585211.html and adapted to SDL2_ttf
#
# - Find SDL2_ttf library and headers
#
# Find module for SDL_ttf 2.0 (http://www.libsdl.org/projects/SDL_ttf/).
# It defines the following variables:
# SDL2_TTF_INCLUDE_DIRS - The location of the headers, e.g., SDL_ttf.h.
# SDL2_TTF_LIBRARIES - The libraries to link against to use SDL2_ttf.
# SDL2_TTF_FOUND - If false, do not try to use SDL2_ttf.
# SDL2_TTF_VERSION_STRING
# Human-readable string containing the version of SDL2_ttf.
#
# Also defined, but not for general use are:
# SDL2_TTF_INCLUDE_DIR - The directory that contains SDL_ttf.h.
# SDL2_TTF_LIBRARY - The location of the SDL2_ttf library.
#
#=============================================================================
# Copyright 2013 Benjamin Eikel
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_SDL2_TTF QUIET SDL2_ttf)
find_path(SDL2_TTF_INCLUDE_DIR
NAMES SDL2/SDL_ttf.h
HINTS
${SDL2_TTF_ROOT}
PATH_SUFFIXES include
)
find_library(SDL2_TTF_LIBRARY
NAMES SDL2_ttf
HINTS
${SDL2_TTF_ROOT}/lib
PATH_SUFFIXES x86
)
if(SDL2_TTF_INCLUDE_DIR AND EXISTS "${SDL2_TTF_INCLUDE_DIR}/SDL2/SDL_ttf.h")
file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL2/SDL_ttf.h" SDL2_TTF_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_TTF_MAJOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL2/SDL_ttf.h" SDL2_TTF_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_TTF_MINOR_VERSION[ \t]+[0-9]+$")
file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL2/SDL_ttf.h" SDL2_TTF_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_TTF_PATCHLEVEL[ \t]+[0-9]+$")
string(REGEX REPLACE "^#define[ \t]+SDL_TTF_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_MAJOR "${SDL2_TTF_VERSION_MAJOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_TTF_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_MINOR "${SDL2_TTF_VERSION_MINOR_LINE}")
string(REGEX REPLACE "^#define[ \t]+SDL_TTF_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_PATCH "${SDL2_TTF_VERSION_PATCH_LINE}")
set(SDL2_TTF_VERSION_STRING ${SDL2_TTF_VERSION_MAJOR}.${SDL2_TTF_VERSION_MINOR}.${SDL2_TTF_VERSION_PATCH})
unset(SDL2_TTF_VERSION_MAJOR_LINE)
unset(SDL2_TTF_VERSION_MINOR_LINE)
unset(SDL2_TTF_VERSION_PATCH_LINE)
unset(SDL2_TTF_VERSION_MAJOR)
unset(SDL2_TTF_VERSION_MINOR)
unset(SDL2_TTF_VERSION_PATCH)
endif()
set(SDL2_TTF_INCLUDE_DIRS ${SDL2_TTF_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR}/SDL2)
set(SDL2_TTF_LIBRARIES ${SDL2_TTF_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL2_ttf
REQUIRED_VARS SDL2_TTF_INCLUDE_DIRS SDL2_TTF_LIBRARIES
VERSION_VAR SDL2_TTF_VERSION_STRING)
mark_as_advanced(SDL2_TTF_INCLUDE_DIR SDL2_TTF_LIBRARY)

View File

@@ -0,0 +1 @@
"C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/vcvarsall.bat"

View File

@@ -0,0 +1,228 @@
cmake_minimum_required (VERSION 2.8)
project (RetroFE)
set(RETROFE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
set(RETROFE_THIRD_PARTY_DIR "${RETROFE_DIR}/ThirdParty")
list(APPEND CMAKE_MODULE_PATH "${RETROFE_DIR}/CMake")
##############################################################
# Setup some variables to help find external libraries
##############################################################
set(SQLITE3_ROOT "${RETROFE_THIRD_PARTY_DIR}/sqlite3")
set(RAPIDXML_ROOT "${RETROFE_THIRD_PARTY_DIR}/rapidxml-1.13")
if(WIN32)
set(CMAKE_PREFIX_PATH "${RETROFE_DIR}/ThirdParty/win32")
set(SDL2_ROOT "${RETROFE_THIRD_PARTY_DIR}/SDL2-2.0.3")
set(SDL2_IMAGE_ROOT "${RETROFE_THIRD_PARTY_DIR}/SDL2_image-2.0.0")
set(SDL2_MIXER_ROOT "${RETROFE_THIRD_PARTY_DIR}/SDL2_mixer-2.0.0")
set(SDL2_TTF_ROOT "${RETROFE_THIRD_PARTY_DIR}/SDL2_ttf-2.0.12")
set(ZLIB_ROOT "${RETROFE_THIRD_PARTY_DIR}/zlib128-dll")
set(GSTREAMER_ROOT "C:/gstreamer/1.0/x86" CACHE STRING "location of where your gstreamer include and lib folders reside")
set(GLIB2_ROOT "${GSTREAMER_ROOT}")
set(DIRENT_INCLUDE_DIR "${RETROFE_THIRD_PARTY_DIR}/dirent-1.20.1/include")
endif()
if(MSVC)
find_package(Glib2 REQUIRED)
find_package(GStreamer REQUIRED)
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
find_package(SDL2_mixer REQUIRED)
find_package(SDL2_ttf REQUIRED)
find_package(ZLIB REQUIRED)
else()
include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)
pkg_search_module(SDL2_IMAGE REQUIRED SDL2_image)
pkg_search_module(SDL2_MIXER REQUIRED SDL2_mixer)
pkg_search_module(SDL2_TTF REQUIRED SDL2_ttf)
pkg_search_module(ZLIB REQUIRED zlib)
pkg_search_module(GSTREAMER REQUIRED gstreamer-1.0 gstbase-1.0)
pkg_search_module(Glib2 REQUIRED glib-2.0 gobject-2.0 gthread-2.0 gmodule-2.0)
find_package(Threads REQUIRED)
endif()
set(RETROFE_INCLUDE_DIRS
"${GLIB2_INCLUDE_DIRS}"
"${GSTREAMER_INCLUDE_DIRS}"
"${SDL2_INCLUDE_DIRS}"
"${SDL2_IMAGE_INCLUDE_DIRS}"
"${SDL2_MIXER_INCLUDE_DIRS}"
"${SDL2_TTF_INCLUDE_DIRS}"
"${ZLIB_INCLUDE_DIRS}"
"${SQLITE3_ROOT}"
"${RAPIDXML_ROOT}"
"${DIRENT_INCLUDE_DIR}"
)
set(RETROFE_LIBRARIES
${GLIB2_LIBRARIES}
${GSTREAMER_LIBRARIES}
${SDL2_LIBRARIES}
${SDL2_IMAGE_LIBRARIES}
${SDL2_MIXER_LIBRARIES}
${SDL2_TTF_LIBRARIES}
${ZLIB_LIBRARIES}
)
if(NOT MSVC)
LIST(APPEND RETROFE_LIBRARIES ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
endif()
set(RETROFE_HEADERS
"${RETROFE_DIR}/Source/Collection/CollectionInfo.h"
"${RETROFE_DIR}/Source/Collection/CollectionInfoBuilder.h"
"${RETROFE_DIR}/Source/Collection/Item.h"
"${RETROFE_DIR}/Source/Collection/MenuParser.h"
"${RETROFE_DIR}/Source/Control/UserInput.h"
"${RETROFE_DIR}/Source/Database/CollectionDatabase.h"
"${RETROFE_DIR}/Source/Database/Configuration.h"
"${RETROFE_DIR}/Source/Database/DB.h"
"${RETROFE_DIR}/Source/Database/MamelistMetadata.h"
"${RETROFE_DIR}/Source/Execute/AttractMode.h"
"${RETROFE_DIR}/Source/Execute/Launcher.h"
"${RETROFE_DIR}/Source/Graphics/Animate/Tween.h"
"${RETROFE_DIR}/Source/Graphics/Animate/TweenTypes.h"
"${RETROFE_DIR}/Source/Graphics/ComponentItemBinding.h"
"${RETROFE_DIR}/Source/Graphics/Component/Container.h"
"${RETROFE_DIR}/Source/Graphics/Component/Component.h"
"${RETROFE_DIR}/Source/Graphics/Component/Image.h"
"${RETROFE_DIR}/Source/Graphics/Component/ImageBuilder.h"
"${RETROFE_DIR}/Source/Graphics/Component/ReloadableMedia.h"
"${RETROFE_DIR}/Source/Graphics/Component/ReloadableText.h"
"${RETROFE_DIR}/Source/Graphics/Component/ScrollingList.h"
"${RETROFE_DIR}/Source/Graphics/Component/Text.h"
"${RETROFE_DIR}/Source/Graphics/Component/VideoComponent.h"
"${RETROFE_DIR}/Source/Graphics/Component/VideoBuilder.h"
"${RETROFE_DIR}/Source/Graphics/Font.h"
"${RETROFE_DIR}/Source/Graphics/FontCache.h"
"${RETROFE_DIR}/Source/Graphics/PageBuilder.h"
"${RETROFE_DIR}/Source/Graphics/MenuNotifierInterface.h"
"${RETROFE_DIR}/Source/Graphics/Page.h"
"${RETROFE_DIR}/Source/Sound/Sound.h"
"${RETROFE_DIR}/Source/Utility/Log.h"
"${RETROFE_DIR}/Source/Utility/Utils.h"
"${RETROFE_DIR}/Source/Video/IVideo.h"
"${RETROFE_DIR}/Source/Video/GStreamerVideo.h"
"${RETROFE_DIR}/Source/Video/VideoFactory.h"
"${RETROFE_DIR}/Source/Graphics/ComponentItemBindingBuilder.h"
"${RETROFE_DIR}/Source/Graphics/ViewInfo.h"
"${RETROFE_DIR}/Source/RetroFE.h"
"${RETROFE_DIR}/Source/SDL.h"
"${RETROFE_DIR}/Source/Version.h"
)
set(RETROFE_SOURCES
"${RETROFE_DIR}/Source/Collection/CollectionInfo.cpp"
"${RETROFE_DIR}/Source/Collection/CollectionInfoBuilder.cpp"
"${RETROFE_DIR}/Source/Collection/Item.cpp"
"${RETROFE_DIR}/Source/Collection/MenuParser.cpp"
"${RETROFE_DIR}/Source/Control/UserInput.cpp"
"${RETROFE_DIR}/Source/Database/CollectionDatabase.cpp"
"${RETROFE_DIR}/Source/Database/Configuration.cpp"
"${RETROFE_DIR}/Source/Database/DB.cpp"
"${RETROFE_DIR}/Source/Database/MamelistMetadata.cpp"
"${RETROFE_DIR}/Source/Execute/AttractMode.cpp"
"${RETROFE_DIR}/Source/Execute/Launcher.cpp"
"${RETROFE_DIR}/Source/Graphics/Animate/Tween.cpp"
"${RETROFE_DIR}/Source/Graphics/Font.cpp"
"${RETROFE_DIR}/Source/Graphics/FontCache.cpp"
"${RETROFE_DIR}/Source/Graphics/PageBuilder.cpp"
"${RETROFE_DIR}/Source/Graphics/Page.cpp"
"${RETROFE_DIR}/Source/Graphics/ViewInfo.cpp"
"${RETROFE_DIR}/Source/Graphics/ComponentItemBindingBuilder.cpp"
"${RETROFE_DIR}/Source/Graphics/ComponentItemBinding.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/Container.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/Component.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/Image.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/ImageBuilder.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/Text.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/ReloadableMedia.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/ReloadableText.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/ScrollingList.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/VideoBuilder.cpp"
"${RETROFE_DIR}/Source/Graphics/Component/VideoComponent.cpp"
"${RETROFE_DIR}/Source/Sound/Sound.cpp"
"${RETROFE_DIR}/Source/Utility/Log.cpp"
"${RETROFE_DIR}/Source/Utility/Utils.cpp"
"${RETROFE_DIR}/Source/Video/GStreamerVideo.cpp"
"${RETROFE_DIR}/Source/Video/VideoFactory.cpp"
"${RETROFE_DIR}/Source/Main.cpp"
"${RETROFE_DIR}/Source/RetroFE.cpp"
"${RETROFE_DIR}/Source/SDL.cpp"
"${RETROFE_DIR}/Source/Version.cpp"
"${SQLITE3_ROOT}/sqlite3.c"
)
set(EXECUTABLE_OUTPUT_PATH "${RETROFE_DIR}/Build" CACHE PATH "Build directory" FORCE)
set(LIBRARY_OUTPUT_PATH "${RETROFE_DIR}/Build" CACHE PATH "Build directory" FORCE)
include_directories(${RETROFE_INCLUDE_DIRS})
add_executable(RetroFE ${RETROFE_SOURCES} ${RETROFE_HEADERS})
target_link_libraries(RetroFE ${RETROFE_LIBRARIES})
set_target_properties(RetroFE PROPERTIES LINKER_LANGUAGE CXX)
add_definitions(-DRETROFE_VERSION_MAJOR=${VERSION_MAJOR})
add_definitions(-DRETROFE_VERSION_MINOR=${VERSION_MINOR})
add_definitions(-DRETROFE_VERSION_BUILD=${VERSION_BUILD})
if(MSVC)
set(CMAKE_DEBUG_POSTFIX "d")
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /WX")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP /WX")
set_target_properties(RetroFE PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
endif()
set(RETROFE_OUTPUT_PATH "../Build/Artifacts/RetroFE")
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "../Build/Artifacts/RetroFE" )
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "../Assets/Environment/Common" "${RETROFE_OUTPUT_PATH}" )
if(WIN32)
set(RETROFE_OUTPUT_CORE_PATH "${RETROFE_OUTPUT_PATH}/Core")
file(GLOB CORE_FILES
"${GSTREAMER_ROOT}/lib/*.dll"
"${GSTREAMER_ROOT}/lib/gstreamer-1.0/*.dll"
"${GSTREAMER_ROOT}/bin/*.dll"
"${SDL2_ROOT}/lib/x86/*.dll"
"${SDL2_IMAGE_ROOT}/lib/x86/*.dll"
"${SDL2_TTF_ROOT}/lib/x86/*.dll"
"${SDL2_MIXER_ROOT}/lib/x86/*.dll"
)
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${RETROFE_OUTPUT_CORE_PATH}" )
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "../Assets/Environment/Windows" "${RETROFE_OUTPUT_PATH}" )
foreach(CORE_FILE ${CORE_FILES})
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CORE_FILE}" "${RETROFE_OUTPUT_CORE_PATH}" )
endforeach()
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "../Build/Debug/RetroFE.exe" "${RETROFE_OUTPUT_CORE_PATH}" )
else()
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "../Assets/Environment/Linux" "${RETROFE_OUTPUT_PATH}" )
ADD_CUSTOM_COMMAND(TARGET RetroFE POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "../Build/RetroFE" "${RETROFE_OUTPUT_PATH}" )
endif()

View File

@@ -0,0 +1,79 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CollectionInfo.h"
#include "../Database/Configuration.h"
#include <sstream>
CollectionInfo::CollectionInfo(std::string name,
std::string listPath,
std::string extensions,
std::string metadataType,
std::string metadataPath)
: Name(name)
, ListPath(listPath)
, Extensions(extensions)
, MetadataType(metadataType)
, MetadataPath(metadataPath)
{
}
CollectionInfo::~CollectionInfo()
{
}
std::string CollectionInfo::GetName() const
{
return Name;
}
std::string CollectionInfo::GetSettingsPath() const
{
return Configuration::GetAbsolutePath() + "/Collections/" + GetName();
}
std::string CollectionInfo::GetListPath() const
{
return ListPath;
}
std::string CollectionInfo::GetMetadataType() const
{
return MetadataType;
}
std::string CollectionInfo::GetMetadataPath() const
{
return MetadataPath;
}
std::string CollectionInfo::GetExtensions() const
{
return Extensions;
}
void CollectionInfo::GetExtensions(std::vector<std::string> &extensions)
{
std::istringstream ss(Extensions);
std::string token;
while(std::getline(ss, token, ','))
{
extensions.push_back(token);
}
}

View File

@@ -0,0 +1,28 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <string>
#include <vector>
class CollectionInfo
{
public:
CollectionInfo(std::string name, std::string listPath, std::string extensions, std::string metadataType, std::string metadataPath);
virtual ~CollectionInfo();
std::string GetName() const;
std::string GetSettingsPath() const;
std::string GetListPath() const;
std::string GetMetadataType() const;
std::string GetMetadataPath() const;
std::string GetExtensions() const;
void GetExtensions(std::vector<std::string> &extensions);
private:
std::string Name;
std::string ListPath;
std::string Extensions;
std::string MetadataType;
std::string MetadataPath;
};

View File

@@ -0,0 +1,141 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CollectionInfoBuilder.h"
#include "CollectionInfo.h"
#include "../Database/Configuration.h"
#include "../Utility/Log.h"
#include <sstream>
#include <vector>
CollectionInfoBuilder::CollectionInfoBuilder(Configuration &c)
: Conf(c)
{
}
CollectionInfoBuilder::~CollectionInfoBuilder()
{
std::map<std::string, CollectionInfo *>::iterator it = InfoMap.begin();
for(it == InfoMap.begin(); it != InfoMap.end(); ++it)
{
delete it->second;
}
InfoMap.clear();
}
bool CollectionInfoBuilder::LoadAllCollections()
{
std::vector<std::string> collections;
Conf.GetChildKeyCrumbs("collections", collections);
if(collections.size() == 0)
{
Logger::Write(Logger::ZONE_ERROR, "Collections", "No collections were found. Please configure Settings.conf");
return false;
}
bool retVal = true;
std::vector<std::string>::iterator it;
for(it = collections.begin(); it != collections.end(); ++it)
{
// todo: There is nothing that should really stop us from creating a collection
// in the main folder. I just need to find some time to look at the impacts if
// I remove this conditional check.
if(*it != "Main")
{
if(ImportCollection(*it))
{
Logger::Write(Logger::ZONE_INFO, "Collections", "Adding collection " + *it);
}
else
{
// Continue processing the rest of the collections if an error occurs during import.
// ImportCollection() will print out an error to the log file.
retVal = false;
}
}
}
return retVal;
}
void CollectionInfoBuilder::GetCollections(std::vector<CollectionInfo *> &collections)
{
std::map<std::string, CollectionInfo *>::iterator InfoMapIt;
for(InfoMapIt = InfoMap.begin(); InfoMapIt != InfoMap.end(); ++InfoMapIt)
{
collections.push_back(InfoMapIt->second);
}
}
bool CollectionInfoBuilder::ImportCollection(std::string name)
{
// create a new instance if one does not exist
if(InfoMap.find(name) != InfoMap.end())
{
return true;
}
std::string listItemsPathKey = "collections." + name + ".list.path";
std::string listFilterKey = "collections." + name + ".list.filter";
std::string extensionsKey = "collections." + name + ".list.extensions";
std::string launcherKey = "collections." + name + ".launcher";
//todo: metadata is not fully not implemented
std::string metadataTypeKey = "collections." + name + ".metadata.type";
std::string metadataPathKey = "collections." + name + ".metadata.path";
std::string listItemsPath;
std::string launcherName;
std::string extensions;
std::string metadataType;
std::string metadataPath;
if(!Conf.GetPropertyAbsolutePath(listItemsPathKey, listItemsPath))
{
Logger::Write(Logger::ZONE_INFO, "Collections", "Property \"" + listItemsPathKey + "\" does not exist. Assuming \"" + name + "\" is a menu");
return false;
}
if(!Conf.GetProperty(extensionsKey, extensions))
{
Logger::Write(Logger::ZONE_INFO, "Collections", "Property \"" + extensionsKey + "\" does not exist. Assuming \"" + name + "\" is a menu");
return false;
}
(void)Conf.GetProperty(metadataTypeKey, metadataType);
(void)Conf.GetProperty(metadataPathKey, metadataPath);
if(!Conf.GetProperty(launcherKey, launcherName))
{
std::stringstream ss;
ss << "Warning: launcher property \""
<< launcherKey
<< "\" points to a launcher that is not configured (launchers."
<< launcherName
<< "). Your collection will be viewable, however you will not be able to "
<< "launch any of the items in your collection.";
Logger::Write(Logger::ZONE_WARNING, "Collections", ss.str());
}
InfoMap[name] = new CollectionInfo(name, listItemsPath, extensions, metadataType, metadataPath);
return (InfoMap[name] != NULL);
}

View File

@@ -0,0 +1,25 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <string>
#include <map>
#include <vector>
class Configuration;
class CollectionInfo;
class CollectionInfoBuilder
{
public:
CollectionInfoBuilder(Configuration &c);
virtual ~CollectionInfoBuilder();
bool LoadAllCollections();
void GetCollections(std::vector<CollectionInfo *> &keys);
private:
bool ImportCollection(std::string name);
std::map<std::string, CollectionInfo *> InfoMap;
Configuration &Conf;
};

View File

@@ -0,0 +1,179 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Item.h"
#include "../Utility/Utils.h"
#include <sstream>
#include <algorithm>
Item::Item()
: NumberPlayers(0)
, NumberButtons(0)
, Leaf(true)
{
}
Item::~Item()
{
}
const std::string Item::GetFileName() const
{
return Utils::GetFileName(FilePath);
}
const std::string& Item::GetFilePath() const
{
return FilePath;
}
void Item::SetFilePath(const std::string& filepath)
{
FilePath = filepath;
}
const std::string& Item::GetLauncher() const
{
return Launcher;
}
void Item::SetLauncher(const std::string& launcher)
{
Launcher = launcher;
}
const std::string& Item::GetManufacturer() const
{
return Manufacturer;
}
void Item::SetManufacturer(const std::string& manufacturer)
{
Manufacturer = manufacturer;
}
const std::string& Item::GetName() const
{
return Name;
}
void Item::SetName(const std::string& name)
{
Name = name;
}
int Item::GetNumberButtons() const
{
return NumberButtons;
}
std::string Item::GetNumberButtonsString()
{
std::stringstream ss;
ss << NumberButtons;
return ss.str();
}
void Item::SetNumberButtons(int numberbuttons)
{
NumberButtons = numberbuttons;
}
int Item::GetNumberPlayers() const
{
return NumberPlayers;
}
std::string Item::GetNumberPlayersString()
{
std::stringstream ss;
ss << NumberButtons;
return ss.str();
}
void Item::SetNumberPlayers(int numberplayers)
{
NumberPlayers = numberplayers;
}
const std::string& Item::GetTitle() const
{
return Title;
}
const std::string& Item::GetLCTitle() const
{
return LCTitle;
}
void Item::SetTitle(const std::string& title)
{
Title = title;
LCTitle = Title;
std::transform(LCTitle.begin(), LCTitle.end(), LCTitle.begin(), ::tolower);
}
const std::string& Item::GetYear() const
{
return Year;
}
void Item::SetYear(const std::string& year)
{
Year = year;
}
bool Item::IsLeaf() const
{
return Leaf;
}
void Item::SetIsLeaf(bool leaf)
{
Leaf = leaf;
}
const std::string& Item::GetFullTitle() const
{
return FullTitle;
}
void Item::SetFullTitle(const std::string& fulltitle)
{
FullTitle = fulltitle;
}
const std::string& Item::GetCloneOf() const
{
return CloneOf;
}
void Item::SetCloneOf(const std::string& cloneOf)
{
CloneOf = cloneOf;
}
bool Item::operator<(const Item &rhs)
{
return LCTitle < rhs.LCTitle;
}
bool Item::operator>(const Item &rhs)
{
return LCTitle > rhs.LCTitle;
}

View File

@@ -0,0 +1,56 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <string>
class Item
{
public:
Item();
virtual ~Item();
const std::string GetFileName() const;
const std::string& GetFilePath() const;
void SetFilePath(const std::string& filepath);
const std::string& GetLauncher() const;
void SetLauncher(const std::string& launcher);
const std::string& GetManufacturer() const;
void SetManufacturer(const std::string& manufacturer);
const std::string& GetName() const;
void SetName(const std::string& name);
int GetNumberButtons() const;
std::string GetNumberButtonsString();
void SetNumberButtons(int numberbuttons);
void SetNumberPlayers(int numberplayers);
int GetNumberPlayers() const;
std::string GetNumberPlayersString();
const std::string& GetTitle() const;
const std::string& GetLCTitle() const;
void SetTitle(const std::string& title);
const std::string& GetYear() const;
void SetYear(const std::string& year);
bool IsLeaf() const;
void SetIsLeaf(bool leaf);
const std::string& GetFullTitle() const;
void SetFullTitle(const std::string& fulltitle);
const std::string& GetCloneOf() const;
void SetCloneOf(const std::string& cloneOf);
bool operator<(const Item& rhs);
bool operator>(const Item& rhs);
private:
std::string Launcher;
std::string FilePath;
std::string Name;
std::string Title;
std::string LCTitle;
std::string FullTitle;
std::string Year;
std::string Manufacturer;
std::string CloneOf;
int NumberPlayers;
int NumberButtons;
bool Leaf;
};

View File

@@ -0,0 +1,121 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MenuParser.h"
#include "Item.h"
#include "../Utility/Log.h"
#include "../Database/Configuration.h"
#include "../Database/CollectionDatabase.h"
#include "../Database/DB.h"
#include <algorithm>
#include <rapidxml.hpp>
#include <fstream>
#include <sstream>
bool VectorSort(const Item *d1, const Item *d2)
{
return d1->GetLCTitle() < d2->GetLCTitle();
}
MenuParser::MenuParser()
{
}
MenuParser::~MenuParser()
{
}
//todo: clean up this method, too much nesting
bool MenuParser::GetMenuItems(CollectionDatabase *cdb, std::string collectionName, std::vector<Item *> &items)
{
bool retVal = false;
//todo: magic string
std::string menuFilename = Configuration::GetAbsolutePath() + "/Collections/" + collectionName + "/Menu.xml";
rapidxml::xml_document<> doc;
rapidxml::xml_node<> * rootNode;
Logger::Write(Logger::ZONE_INFO, "Menu", "Checking if menu exists at \"" + menuFilename + "\"");
try
{
std::ifstream file(menuFilename.c_str());
// gracefully exit if there is no menu file for the pa
if(file.good())
{
Logger::Write(Logger::ZONE_INFO, "Menu", "Found menu");
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
buffer.push_back('\0');
doc.parse<0>(&buffer[0]);
rootNode = doc.first_node("menu");
for (rapidxml::xml_node<> * itemNode = rootNode->first_node("item"); itemNode; itemNode = itemNode->next_sibling())
{
rapidxml::xml_attribute<> *collectionAttribute = itemNode->first_attribute("collection");
rapidxml::xml_attribute<> *importAttribute = itemNode->first_attribute("import");
if(!collectionAttribute)
{
retVal = false;
Logger::Write(Logger::ZONE_ERROR, "Menu", "Menu item tag is missing collection attribute");
break;
}
else
{
//todo: too much nesting! Ack!
std::string import;
if(importAttribute)
{
import = importAttribute->value();
}
if(import != "true")
{
//todo, check for empty string
std::string title = collectionAttribute->value();
Item *item = new Item();
item->SetTitle(title);
item->SetFullTitle(title);
item->SetName(collectionAttribute->value());
item->SetIsLeaf(false);
items.push_back(item);
}
else
{
std::string collectionName = collectionAttribute->value();
Logger::Write(Logger::ZONE_INFO, "Menu", "Loading collection into menu: " + collectionName);
cdb->GetCollection(collectionAttribute->value(), items);
}
}
}
std::sort( items.begin(), items.end(), VectorSort);
retVal = true;
}
}
catch(std::ifstream::failure &e)
{
std::stringstream ss;
ss << "Unable to open menu file \"" << menuFilename << "\": " << e.what();
Logger::Write(Logger::ZONE_ERROR, "Menu", ss.str());
}
return retVal;
}

View File

@@ -0,0 +1,17 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Item.h"
#include <vector>
class CollectionDatabase;
class MenuParser
{
public:
MenuParser();
virtual ~MenuParser();
bool GetMenuItems(CollectionDatabase *cdb, std::string collectionName, std::vector<Item *> &items);
};

View File

@@ -0,0 +1,91 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "UserInput.h"
#include "../Database/Configuration.h"
#include "../Utility/Log.h"
UserInput::UserInput(Configuration &c)
: Config(c)
{
}
UserInput::~UserInput()
{
}
bool UserInput::Initialize()
{
bool retVal = true;
retVal = MapKey("nextItem", KeyCodeNextItem) && retVal;
retVal = MapKey("previousItem", KeyCodePreviousItem) && retVal;
retVal = MapKey("pageDown", KeyCodePageDown) && retVal;
retVal = MapKey("pageUp", KeyCodePageUp) && retVal;
retVal = MapKey("select", KeyCodeSelect) && retVal;
retVal = MapKey("back", KeyCodeBack) && retVal;
retVal = MapKey("quit", KeyCodeQuit) && retVal;
// these features will need to be implemented at a later time
// retVal = MapKey("admin", KeyCodeAdminMode) && retVal;
// retVal = MapKey("remove", KeyCodeHideItem) && retVal;
return retVal;
}
SDL_Scancode UserInput::GetScancode(KeyCode_E key)
{
SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
std::map<KeyCode_E, SDL_Scancode>::iterator it = KeyMap.find(key);
if(it != KeyMap.end())
{
scancode = it->second;
}
return scancode;
}
bool UserInput::MapKey(std::string keyDescription, KeyCode_E key)
{
bool retVal = false;
SDL_Scancode scanCode;
std::string description;
std::string configKey = "controls." + keyDescription;
if(!Config.GetProperty(configKey, description))
{
Logger::Write(Logger::ZONE_ERROR, "Configuration", "Missing property " + configKey);
}
else
{
scanCode = SDL_GetScancodeFromName(description.c_str());
if(scanCode == SDL_SCANCODE_UNKNOWN)
{
Logger::Write(Logger::ZONE_ERROR, "Configuration", "Unsupported property value for " + configKey + "(" + description + "). See Documentation/Keycodes.txt for valid inputs");
}
else
{
KeyMap[key] = scanCode;
retVal = true;
}
}
return retVal;
}

View File

@@ -0,0 +1,38 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <map>
#include <SDL2/SDL.h>
#include <string>
class Configuration;
class UserInput
{
public:
enum KeyCode_E
{
KeyCodeNextItem,
KeyCodePreviousItem,
KeyCodeSelect,
KeyCodeBack,
KeyCodePageDown,
KeyCodePageUp,
KeyCodeAdminMode,
KeyCodeHideItem,
KeyCodeQuit
};
UserInput(Configuration &c);
virtual ~UserInput();
bool Initialize();
SDL_Scancode GetScancode(KeyCode_E key);
private:
bool MapKey(std::string keyDescription, KeyCode_E key);
std::map<KeyCode_E, SDL_Scancode> KeyMap;
Configuration &Config;
};

View File

@@ -0,0 +1,759 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CollectionDatabase.h"
#include "../Collection/CollectionInfoBuilder.h"
#include "../Collection/CollectionInfo.h"
#include "../Collection/Item.h"
#include "../Utility/Log.h"
#include "../Utility/Utils.h"
#include "MamelistMetadata.h"
#include "Configuration.h"
#include "DB.h"
#include <algorithm>
#include <dirent.h>
#include <fstream>
#include <list>
#include <rapidxml.hpp>
#include <sstream>
#include <string>
#include <map>
#include <sys/types.h>
#include <sqlite3.h>
#include <zlib.h>
#include <exception>
CollectionDatabase::CollectionDatabase(DB &db, Configuration &c)
: Config(c)
, DBInstance(db)
{
}
CollectionDatabase::~CollectionDatabase()
{
}
bool CollectionDatabase::ResetDatabase()
{
bool retVal = true;
int rc;
char *error = NULL;
sqlite3 *handle = DBInstance.GetHandle();
Logger::Write(Logger::ZONE_INFO, "Database", "Erasing");
std::string sql;
sql.append("DROP TABLE IF EXISTS CollectionItems;");
sql.append("DROP TABLE IF EXISTS Meta;");
sql.append("DROP TABLE IF EXISTS Collections;");
rc = sqlite3_exec(handle, sql.c_str(), NULL, 0, &error);
if(rc != SQLITE_OK)
{
std::stringstream ss;
ss << "Unable to create Configurations table. Error: " << error;
Logger::Write(Logger::ZONE_ERROR, "Database", ss.str());
retVal = false;
}
return retVal;
}
bool CollectionDatabase::Initialize()
{
int rc;
char *error = NULL;
sqlite3 *handle = DBInstance.GetHandle();
std::string sql;
sql.append("CREATE TABLE IF NOT EXISTS CollectionItems(");
sql.append("collectionName TEXT KEY,");
sql.append("filePath TEXT NOT NULL DEFAULT '',");
sql.append("name TEXT NOT NULL DEFAULT '',");
sql.append("hidden INT NOT NULL DEFAULT 0);");
sql.append("CREATE TABLE IF NOT EXISTS Meta(");
sql.append("collectionName TEXT KEY,");
sql.append("name TEXT NOT NULL DEFAULT '',");
sql.append("title TEXT NOT NULL DEFAULT '',");
sql.append("year TEXT NOT NULL DEFAULT '',");
sql.append("manufacturer TEXT NOT NULL DEFAULT '',");
sql.append("cloneOf TEXT NOT NULL DEFAULT '',");
sql.append("players INTEGER,");
sql.append("buttons INTEGER);");
sql.append("CREATE UNIQUE INDEX IF NOT EXISTS MetaUniqueId ON Meta(collectionName, name);");
sql.append("CREATE TABLE IF NOT EXISTS Collections(");
sql.append("collectionName TEXT KEY,");
sql.append("crc32 UNSIGNED INTEGER NOT NULL DEFAULT 0);");
sql.append("CREATE UNIQUE INDEX IF NOT EXISTS CollectionsUniqueId ON Collections(collectionName);");
rc = sqlite3_exec(handle, sql.c_str(), NULL, 0, &error);
if(rc != SQLITE_OK)
{
std::stringstream ss;
ss << "Unable to create Configurations table. Error: " << error;
Logger::Write(Logger::ZONE_ERROR, "Database", ss.str());
return false;
}
return true;
}
bool CollectionDatabase::Import()
{
bool retVal = true;
// should keep allocation here
CollectionInfoBuilder cib(Config);
(void)cib.LoadAllCollections();
std::vector<CollectionInfo *> collections;
cib.GetCollections(collections);
std::vector<CollectionInfo *>::iterator it;
for(it = collections.begin(); it != collections.end() && retVal; ++it)
{
CollectionInfo *info = *it;
std::string title = info->GetName();
unsigned long crc32 = CalculateCollectionCrc32(info);
std::stringstream crcStr;
crcStr << crc32;
if(title != "Main")
{
if(CollectionChanged(info, crc32))
{
std::string msg = "Detected collection \"" + title + "\" has changed (new CRC: " + crcStr.str() + "). Rebuilding database for this collection.";
Logger::Write(Logger::ZONE_INFO, "Database", msg);
(void)ImportDirectory(info, crc32);
retVal = true;
}
else
{
std::stringstream ss;
std::string msg = "Collection \"" + title + "\" has not changed (CRC: " + crcStr.str() + "). Using existing database settings.";
Logger::Write(Logger::ZONE_INFO, "Database", msg);
}
}
//std::cout << "Importing collection metadata for " << info->GetFullTitle() << " (collections." << info->GetName() << ")" << std::endl;
//ImportMetadata(info);
}
Logger::Write(Logger::ZONE_INFO, "Database", "COMPLETE");
Sleep(1000);
return retVal;
}
unsigned long CollectionDatabase::CalculateCollectionCrc32(CollectionInfo *info)
{
unsigned long crc = crc32(0L, Z_NULL, 0);
// start off by reading all of the contents in the collection configuration folders
std::string settingsFile = info->GetSettingsPath() + "/Settings.conf";
crc = CrcFile(settingsFile, crc);
std::string includeFile = info->GetSettingsPath() + "/Include.txt";
crc = CrcFile(includeFile, crc);
std::string excludeFile = info->GetSettingsPath() + "/Exclude.txt";
crc = CrcFile(excludeFile, crc);
std::string mamelistFile = info->GetSettingsPath() + "/Mamelist.xml";
crc = CrcFile(mamelistFile, crc);
DIR *dp;
struct dirent *dirp;
std::string path = info->GetListPath();
dp = opendir(path.c_str());
if(dp == NULL)
{
Logger::Write(Logger::ZONE_ERROR, "Database", "Could not read directory for caching \"" + info->GetListPath() + "\"");
return crc;
}
std::vector<std::string> extensions;
info->GetExtensions(extensions);
std::vector<std::string>::iterator extensionsIt;
// md5sum each filename for the matching extension
while((dirp = readdir(dp)) != NULL)
{
std::string file = dirp->d_name;
for(extensionsIt = extensions.begin(); extensionsIt != extensions.end(); ++extensionsIt)
{
std::string comparator = "." + *extensionsIt;
int start = file.length() - comparator.length() + 1;
if(start >= 0 && file.compare(start, comparator.length(), *extensionsIt) == 0)
{
std::string filename = dirp->d_name;
filename.append("\n");
crc = crc32(crc, (const unsigned char *)filename.c_str(), (unsigned int)filename.length());
}
}
}
return crc;
}
unsigned long CollectionDatabase::CrcFile(std::string file, unsigned long crc)
{
// CRC both the filename and its contents
crc = crc32(crc, (const unsigned char *)file.c_str(), (unsigned int)file.length());
std::ifstream ifFile(file.c_str());
if(ifFile.good())
{
std::stringstream ss;
ss << ifFile.rdbuf();
crc = crc32(crc, (const unsigned char *)ss.str().c_str(), (unsigned int)ss.str().length());
Logger::Write(Logger::ZONE_INFO, "Database", "Crcing \"" + file + "\"");
ifFile.close();
}
return crc;
}
bool CollectionDatabase::CollectionChanged(CollectionInfo *info, unsigned long crc32)
{
bool retVal = true;
sqlite3 *handle = DBInstance.GetHandle();
int rc;
sqlite3_stmt *stmt;
sqlite3_prepare_v2(handle,
"SELECT crc32 "
"FROM Collections WHERE collectionName=? and crc32=?;",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 2, crc32);
rc = sqlite3_step(stmt);
if(rc == SQLITE_ROW)
{
retVal = false;
}
return retVal;
}
bool CollectionDatabase::SetHidden(std::string collectionName, Item *item, bool hidden)
{
bool retVal = true;
char *error = NULL;
sqlite3 *handle = DBInstance.GetHandle();
std::string mode = (hidden) ? "hidden":"visible";
int isHidden = (hidden)?1:0;
Logger::Write(Logger::ZONE_DEBUG, "Database", "Marking \"" + item->GetFullTitle() + "\" " + mode);
sqlite3_stmt *stmt;
sqlite3_prepare_v2(handle,
"UPDATE CollectionItems SET hidden=? WHERE collectionName=? AND name=?;",
-1, &stmt, 0);
sqlite3_bind_int(stmt, 1, isHidden);
sqlite3_bind_text(stmt, 2, collectionName.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, item->GetFullTitle().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_exec(handle, "COMMIT TRANSACTION;", NULL, NULL, &error);
return retVal;
}
//todo: This file needs MASSIVE REFACTORING!
bool CollectionDatabase::ImportDirectory(CollectionInfo *info, unsigned long crc32)
{
DIR *dp;
struct dirent *dirp;
std::string path = info->GetListPath();
std::map<std::string, Item *> includeFilter;
std::map<std::string, Item *> excludeFilter;
std::map<std::string, Item *> includeList;
std::map<std::string, Item *> metaList;
bool retVal = true;
char *error = NULL;
sqlite3 *handle = DBInstance.GetHandle();
std::string includeFile = Configuration::GetAbsolutePath() + "/Collections/" + info->GetName() + "/Include.txt";
std::string excludeFile = Configuration::GetAbsolutePath() + "/Collections/" + info->GetName() + "/Exclude.txt";
std::string includeHyperListFile = Configuration::GetAbsolutePath() + "/Collections/" + info->GetName() + "/Include.xml";
if(!ImportBasicList(info, includeFile, includeFilter))
{
ImportHyperList(info, includeHyperListFile, includeFilter);
}
//todo: this shouldn't be read twice, perform a copy
ImportHyperList(info, includeHyperListFile, metaList);
(void)ImportBasicList(info, excludeFile, excludeFilter);
dp = opendir(path.c_str());
std::vector<std::string> extensions;
info->GetExtensions(extensions);
std::vector<std::string>::iterator extensionsIt;
if(dp == NULL)
{
Logger::Write(Logger::ZONE_ERROR, "Database", "Could not read directory \"" + info->GetListPath() + "\"");
//todo: store into a database
}
else
{
while((dirp = readdir(dp)) != NULL)
{
std::string file = dirp->d_name;
Utils::NormalizeBackSlashes(file);
size_t position = file.find_last_of(".");
std::string basename = (std::string::npos == position)? file : file.substr(0, position);
if((includeFilter.size() == 0 || includeFilter.find(basename) != includeFilter.end()) &&
(excludeFilter.size() == 0 || excludeFilter.find(basename) == excludeFilter.end()))
{
for(extensionsIt = extensions.begin(); extensionsIt != extensions.end(); ++extensionsIt)
{
std::string comparator = "." + *extensionsIt;
int start = file.length() - comparator.length() + 1;
if(start >= 0)
{
if(file.compare(start, comparator.length(), *extensionsIt) == 0)
{
if(includeList.find(basename) == includeList.end())
{
Item *i = new Item();
i->SetFullTitle(file);
includeList[basename] = i;
}
if(metaList.find(basename) == metaList.end())
{
Item *i = new Item();
i->SetFullTitle(file);
metaList[basename] = i;
}
}
}
}
}
}
}
while(includeFilter.size() > 0)
{
std::map<std::string, Item *>::iterator it = includeFilter.begin();
delete it->second;
includeFilter.erase(it);
}
while(excludeFilter.size() > 0)
{
std::map<std::string, Item *>::iterator it = excludeFilter.begin();
delete it->second;
excludeFilter.erase(it);
}
Logger::Write(Logger::ZONE_INFO, "Database", "Scanning to import \"" + path + "\"");
sqlite3_exec(handle, "BEGIN IMMEDIATE TRANSACTION;", NULL, NULL, &error);
sqlite3_stmt *stmt;
sqlite3_prepare_v2(handle,
"DELETE FROM Collections WHERE collectionName=?;",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_prepare_v2(handle,
"DELETE FROM CollectionItems WHERE collectionName=?;",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_prepare_v2(handle,
"DELETE FROM Meta WHERE collectionName=?;",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
if(sqlite3_exec(handle, "COMMIT TRANSACTION;", NULL, NULL, &error) != SQLITE_OK)
{
std::stringstream ss;
ss << "Updating cache collection failure " << error;
Logger::Write(Logger::ZONE_ERROR, "Database", ss.str());
retVal = false;
}
std::map<std::string, Item *>::iterator it;
if(sqlite3_exec(handle, "BEGIN IMMEDIATE TRANSACTION;", NULL, NULL, &error) != SQLITE_OK)
{
std::stringstream ss;
ss << "Delete cache collection failure " << error;
Logger::Write(Logger::ZONE_ERROR, "Database", ss.str());
retVal = false;
}
for(it = includeList.begin(); it != includeList.end(); it++)
{
std::string basename = it->first;
Item *file = it->second;
std::string name = file->GetFullTitle();
Utils::NormalizeBackSlashes(name);
file->SetFullTitle(name);
sqlite3_prepare_v2(handle,
"INSERT OR REPLACE INTO CollectionItems (collectionName, filePath, name) VALUES (?,?,?);",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, file->GetFullTitle().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, basename.c_str(), -1, SQLITE_TRANSIENT);
//todo: better error handling for all of these messages
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
for(it = metaList.begin(); it != metaList.end(); it++)
{
std::string basename = it->first;
Item *file = it->second;
sqlite3_prepare_v2(handle,
"INSERT OR REPLACE INTO Meta (collectionName, name, title, year, manufacturer, cloneOf, players, buttons) VALUES (?,?,?,?,?,?,?,?);",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, basename.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, basename.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 4, file->GetYear().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 5, file->GetManufacturer().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 6, file->GetCloneOf().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 7, file->GetNumberPlayersString().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 8, file->GetNumberButtonsString().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
sqlite3_prepare_v2(handle,
"INSERT OR REPLACE INTO Collections (collectionName, crc32) VALUES (?,?);",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, info->GetName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 2, crc32);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
if(sqlite3_exec(handle, "COMMIT TRANSACTION;", NULL, NULL, &error) != SQLITE_OK)
{
std::stringstream ss;
ss << "Updating cache collection failure " << error;
Logger::Write(Logger::ZONE_ERROR, "Database", ss.str());
retVal = false;
}
Logger::Write(Logger::ZONE_INFO, "Database", "Imported files from \"" + path + "\" into database");
//todo: create a helper method to get this file directly (copy paste hazard)
std::string mamelistFile = info->GetSettingsPath() + "/Mamelist.xml";
std::ifstream infile(mamelistFile.c_str());
if(infile.good())
{
Logger::Write(Logger::ZONE_INFO, "Database", "Updating Mamelist metadata for \"" + info->GetName() + "\" (\"" + mamelistFile + "\") into database. This will take a while...");
MamelistMetadata mld(DBInstance);
mld.Import(mamelistFile, info->GetName());
}
infile.close();
while(includeList.size() > 0)
{
std::map<std::string, Item *>::iterator it = includeList.begin();
delete it->second;
includeList.erase(it);
}
while(metaList.size() > 0)
{
std::map<std::string, Item *>::iterator it = metaList.begin();
delete it->second;
metaList.erase(it);
}
return retVal;
}
bool CollectionDatabase::ImportBasicList(CollectionInfo *info, std::string file, std::map<std::string, Item *> &list)
{
bool retVal = false;
Logger::Write(Logger::ZONE_DEBUG, "Database", "Checking to see if \"" + file + "\" exists");
std::ifstream includeStream(file.c_str());
if (includeStream.good())
{
Logger::Write(Logger::ZONE_DEBUG, "Database", "Importing \"" + file + "\"");
std::string line;
while(std::getline(includeStream, line))
{
if(list.find(line) == list.end())
{
Item *i = new Item();
line.erase( std::remove(line.begin(), line.end(), '\r'), line.end() );
i->SetFullTitle(line);
list[line] = i;
Logger::Write(Logger::ZONE_DEBUG, "Database", "Including \"" + line + "\" (if file exists)");
}
}
retVal = true;
}
return retVal;
}
bool CollectionDatabase::ImportHyperList(CollectionInfo *info, std::string hyperlistFile, std::map<std::string, Item *> &list)
{
bool retVal = false;
rapidxml::xml_document<> doc;
std::ifstream file(hyperlistFile.c_str());
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
Logger::Write(Logger::ZONE_DEBUG, "Database", "Checking to see if \"" + hyperlistFile + "\" exists");
if(!file.good())
{
Logger::Write(Logger::ZONE_INFO, "Database", "Could not find HyperList file: " + hyperlistFile);
return retVal;
}
try
{
Logger::Write(Logger::ZONE_INFO, "Database", "Importing: " + hyperlistFile);
buffer.push_back('\0');
doc.parse<0>(&buffer[0]);
rapidxml::xml_node<> *root = doc.first_node("menu");
if(!root)
{
Logger::Write(Logger::ZONE_ERROR, "CollectionDatabase", "Does not appear to be a HyperList file (missing <menu> tag)");
return NULL;
}
else
{
for(rapidxml::xml_node<> *game = root->first_node("game"); game; game = game->next_sibling("game"))
{
rapidxml::xml_attribute<> *nameXml = game->first_attribute("name");
rapidxml::xml_node<> *descriptionXml = game->first_node("description");
rapidxml::xml_node<> *cloneofXml = game->first_node("cloneof");
rapidxml::xml_node<> *crcXml = game->first_node("crc");
rapidxml::xml_node<> *manufacturerXml = game->first_node("manufacturer");
rapidxml::xml_node<> *yearXml = game->first_node("year");
rapidxml::xml_node<> *genreXml = game->first_node("genre");
rapidxml::xml_node<> *ratingXml = game->first_node("rating");
rapidxml::xml_node<> *enabledXml = game->first_node("enabled");
std::string name = (nameXml) ? nameXml->value() : "";
std::string description = (descriptionXml) ? descriptionXml->value() : "";
std::string crc = (crcXml) ? crcXml->value() : "";
std::string cloneOf = (cloneofXml) ? cloneofXml->value() : "";
std::string manufacturer = (manufacturerXml) ? manufacturerXml->value() : "";
std::string year = (yearXml) ? yearXml->value() : "";
std::string genre = (genreXml) ? genreXml->value() : "";
std::string rating = (ratingXml) ? ratingXml->value() : "";
std::string enabled = (enabledXml) ? enabledXml->value() : "";
if(name.length() > 0 && list.find(name) == list.end())
{
Item *i = new Item();
i->SetFullTitle(name);
i->SetYear(year);
i->SetManufacturer(manufacturer);
i->SetCloneOf(cloneOf);
list[name] = i;
Logger::Write(Logger::ZONE_DEBUG, "Database", "Including \"" + name + "\" (if file exists)");
}
}
}
}
catch(rapidxml::parse_error &e)
{
std::string what = e.what();
long line = static_cast<long>(std::count(&buffer.front(), e.where<char>(), char('\n')) + 1);
std::stringstream ss;
ss << "Could not parse layout file. [Line: " << line << "] Reason: " << e.what();
Logger::Write(Logger::ZONE_ERROR, "Layout", ss.str());
}
catch(std::exception &e)
{
std::string what = e.what();
Logger::Write(Logger::ZONE_ERROR, "Layout", "Could not parse layout file. Reason: " + what);
}
return retVal;
}
/*
bool CollectionDatabase::ImportMetadata(CollectionInfo *info)
{
bool retVal = true;
std::string type = info->GetMetadataType();
if(type.compare("mamelist") == 0)
{
MamelistMetadata meta;
//todo: pass in collectionName
retVal = meta.Import(info->GetMetadataPath(), "arcade");
}
else if(!type.empty())
{
std::stringstream ss;
ss << "Unsupported metadata type \"" << type << "\" for " << info->GetFullTitle() << " (collections." << info->GetName() << ".metadata.type)" << std::endl;
Log::Write(Log::ERROR, "Database", ss.str());
retVal = false;
}
return retVal;
}
*/
bool CollectionDatabase::GetCollection(std::string collectionName, std::vector<Item *> &list)
{
bool retVal = true;
sqlite3 *handle = DBInstance.GetHandle();
int rc;
sqlite3_stmt *stmt;
bool showParenthesis = true;
bool showSquareBrackets = true;
(void)Config.GetProperty("showParenthesis", showParenthesis);
(void)Config.GetProperty("showSquareBrackets", showSquareBrackets);
//todo: program crashes if this query fails
sqlite3_prepare_v2(handle,
"SELECT DISTINCT CollectionItems.filePath, CollectionItems.name, Meta.title, Meta.year, Meta.manufacturer, Meta.players, Meta.buttons, Meta.cloneOf "
"FROM CollectionItems, Meta WHERE CollectionItems.collectionName=? AND Meta.collectionName=? AND CollectionItems.name=Meta.name AND CollectionItems.hidden=0 ORDER BY title ASC;",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, collectionName.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, collectionName.c_str(), -1, SQLITE_TRANSIENT);
rc = sqlite3_step(stmt);
while(rc == SQLITE_ROW)
{
std::string filePath = (char *)sqlite3_column_text(stmt, 0);
std::string name = (char *)sqlite3_column_text(stmt, 1);
std::string fullTitle = (char *)sqlite3_column_text(stmt, 2);
std::string year = (char *)sqlite3_column_text(stmt, 3);
std::string manufacturer = (char *)sqlite3_column_text(stmt, 4);
int numberPlayers = (int)sqlite3_column_int(stmt, 5);
int numberButtons = (int)sqlite3_column_int(stmt, 6);
std::string cloneOf = (char *)sqlite3_column_text(stmt, 7);
std::string launcher;
std::string title = fullTitle;
if(!showParenthesis)
{
std::string::size_type firstPos = title.find_first_of("(");
std::string::size_type secondPos = title.find_first_of(")", firstPos);
while(firstPos != std::string::npos && secondPos != std::string::npos)
{
firstPos = title.find_first_of("(");
secondPos = title.find_first_of(")", firstPos);
if (firstPos != std::string::npos)
{
title.erase(firstPos, (secondPos - firstPos) + 1);
}
}
}
if(!showSquareBrackets)
{
std::string::size_type firstPos = title.find_first_of("[");
std::string::size_type secondPos = title.find_first_of("]", firstPos);
while(firstPos != std::string::npos && secondPos != std::string::npos)
{
firstPos = title.find_first_of("[");
secondPos = title.find_first_of("]", firstPos);
if (firstPos != std::string::npos && secondPos != std::string::npos)
{
title.erase(firstPos, (secondPos - firstPos) + 1);
}
}
}
Item *item = new Item();
item->SetFilePath(filePath);
item->SetName(name);
item->SetTitle(title);
item->SetFullTitle(fullTitle);
item->SetYear(year);
item->SetManufacturer(manufacturer);
item->SetNumberPlayers(numberPlayers);
item->SetNumberButtons(numberButtons);
item->SetCloneOf(cloneOf);
//std::cout << "loading " << title << std::endl;
if(Config.GetProperty("collections." + collectionName + ".launcher", launcher))
{
item->SetLauncher(launcher);
}
list.push_back(item);
rc = sqlite3_step(stmt);
}
//todo: query the metadata table to populate each item
return retVal;
}

View File

@@ -0,0 +1,44 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
class DB;
class Configuration;
class CollectionInfo;
class Item;
class CollectionDatabase
{
public:
CollectionDatabase(DB &db, Configuration &c);
virtual ~CollectionDatabase();
bool Initialize();
bool Import();
bool ResetDatabase();
bool GetCollection(std::string collectionName, std::vector<Item *> &list);
bool SetHidden(std::string collectionName, Item *item, bool hidden);
private:
unsigned long CalculateCollectionCrc32(CollectionInfo *info);
bool CollectionChanged(CollectionInfo *info, unsigned long crc32);
unsigned long CrcFile(std::string file, unsigned long crc);
// bool ImportMetadata(CollectionInfo *info);
bool ImportDirectory(CollectionInfo *info, unsigned long crc32);
bool ImportBasicList(CollectionInfo *info,
std::string file,
std::map<std::string, Item *> &list);
bool ImportHyperList(CollectionInfo *info,
std::string file,
std::map<std::string, Item *> &list);
std::map<std::string, Item *> *ImportHyperList(CollectionInfo *info);
Configuration &Config;
DB &DBInstance;
};

View File

@@ -0,0 +1,345 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Configuration.h"
#include "../Utility/Log.h"
#include "../Utility/Utils.h"
#include <algorithm>
#include <locale>
#include <fstream>
#include <sstream>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <unistd.h>
#endif
std::string Configuration::AbsolutePath;
Configuration::Configuration()
: Verbose(false)
{
}
Configuration::~Configuration()
{
}
void Configuration::Initialize()
{
const char *environment = std::getenv("RETROFE_PATH");
std::string environmentStr;
if (environment != NULL)
{
environmentStr = environment;
Configuration::SetAbsolutePath(environment);
}
else
{
#ifdef WIN32
HMODULE hModule = GetModuleHandle(NULL);
CHAR exe[MAX_PATH];
GetModuleFileName(hModule, exe, MAX_PATH);
std::string sPath(exe);
sPath = Utils::GetDirectory(sPath);
sPath = Utils::GetParentDirectory(sPath);
#else
char exepath[1024];
sprintf(exepath, "/proc/%d/exe", getpid());
readlink(exepath, exepath, sizeof(exepath));
std::string sPath(exepath);
sPath = Utils::GetDirectory(sPath);
#endif
Configuration::SetAbsolutePath(sPath);
}
}
bool Configuration::Import(std::string keyPrefix, std::string file)
{
bool retVal = true;
int lineCount = 0;
std::string line;
Logger::Write(Logger::ZONE_INFO, "Configuration", "Importing " + file);
std::ifstream ifs(file.c_str());
if (!ifs.is_open())
{
Logger::Write(Logger::ZONE_ERROR, "Configuration", "Could not open " + file);
return false;
}
while (std::getline (ifs, line))
{
lineCount++;
retVal = retVal && ParseLine(keyPrefix, line, lineCount);
}
ifs.close();
return retVal;
}
bool Configuration::ParseLine(std::string keyPrefix, std::string line, int lineCount)
{
bool retVal = false;
std::string key;
std::string value;
size_t position;
std::string delimiter = "=";
// strip out any comments
if((position = line.find("#")) != std::string::npos)
{
line = line.substr(0, position);
}
// unix only wants \n. Windows uses \r\n. Strip off the \r for unix.
line.erase( std::remove(line.begin(), line.end(), '\r'), line.end() );
if(line.empty() || (line.find_first_not_of(" \t\r") == std::string::npos))
{
retVal = true;
}
// all configuration fields must have an assignment operator
else if((position = line.find(delimiter)) != std::string::npos)
{
if(keyPrefix.size() != 0)
{
keyPrefix += ".";
}
key = keyPrefix + line.substr(0, position);
key = TrimEnds(key);
value = line.substr(position + delimiter.length(), line.length());
value = TrimEnds(value);
Properties.insert(PropertiesPair(key, value));
std::stringstream ss;
ss << "Dump: " << "\"" << key << "\" = \"" << value << "\"";
Logger::Write(Logger::ZONE_INFO, "Configuration", ss.str());
retVal = true;
}
else
{
std::stringstream ss;
ss << "Missing an assignment operator (=) on line " << lineCount;
Logger::Write(Logger::ZONE_ERROR, "Configuration", ss.str());
}
return retVal;
}
std::string Configuration::TrimEnds(std::string str)
{
// strip off any initial tabs or spaces
size_t trimStart = str.find_first_not_of(" \t");
if(trimStart != std::string::npos)
{
size_t trimEnd = str.find_last_not_of(" \t");
str = str.substr(trimStart, trimEnd - trimStart + 1);
}
return str;
}
bool Configuration::GetProperty(std::string key, std::string &value)
{
bool retVal = false;
if(Properties.find(key) != Properties.end())
{
value = Properties[key];
retVal = true;
}
else if(Verbose)
{
Logger::Write(Logger::ZONE_DEBUG, "Configuration", "Missing property " + key);
}
return retVal;
}
bool Configuration::GetProperty(std::string key, int &value)
{
std::string strValue;
bool retVal = GetProperty(key, strValue);
if(retVal)
{
std::stringstream ss;
ss << strValue;
ss >> value;
}
return retVal;
}
bool Configuration::GetProperty(std::string key, bool &value)
{
std::string strValue;
bool retVal = GetProperty(key, strValue);
if(retVal)
{
std::stringstream ss;
ss << strValue;
for(unsigned int i=0; i < strValue.length(); ++i)
{
std::locale loc;
strValue[i] = std::tolower(strValue[i], loc);
}
if(!strValue.compare("yes") || !strValue.compare("true"))
{
value = true;
}
else
{
value = false;
}
}
return retVal;
}
void Configuration::SetProperty(std::string key, std::string value)
{
Properties[key] = value;
}
bool Configuration::PropertyExists(std::string key)
{
return (Properties.find(key) != Properties.end());
}
bool Configuration::PropertyPrefixExists(std::string key)
{
PropertiesType::iterator it;
for(it = Properties.begin(); it != Properties.end(); ++it)
{
std::string search = key + ".";
if(it->first.compare(0, search.length(), search) == 0)
{
return true;
}
}
return false;
}
void Configuration::GetChildKeyCrumbs(std::string parent, std::vector<std::string> &children)
{
PropertiesType::iterator it;
for(it = Properties.begin(); it != Properties.end(); ++it)
{
std::string search = parent + ".";
if(it->first.compare(0, search.length(), search) == 0)
{
std::string crumb = Utils::Replace(it->first, search, "");
std::size_t end = crumb.find_first_of(".");
if(end != std::string::npos)
{
crumb = crumb.substr(0, end);
}
if(std::find(children.begin(), children.end(), crumb) == children.end())
{
children.push_back(crumb);
}
}
}
}
std::string Configuration::ConvertToAbsolutePath(std::string prefix, std::string path)
{
char first = ' ';
char second = ' ';
if(path.length() >= 0)
{
first = path.c_str()[0];
}
if(path.length() >= 1)
{
second = path.c_str()[1];
}
// check to see if it is already an absolute path
if((first != '/') &&
(first != '\\') &&
//(first != '.') &&
(second != ':'))
{
path = prefix + "/" + path;
}
return path;
}
bool Configuration::GetPropertyAbsolutePath(std::string key, std::string &value)
{
bool retVal = GetProperty(key, value);
if(retVal)
{
value = ConvertToAbsolutePath(GetAbsolutePath(), value);
}
return retVal;
}
void Configuration::SetAbsolutePath(std::string absolutePath)
{
AbsolutePath = absolutePath;
}
std::string Configuration::GetAbsolutePath()
{
return AbsolutePath;
}
bool Configuration::IsVerbose() const
{
return Verbose;
}
void Configuration::SetVerbose(bool verbose)
{
this->Verbose = verbose;
}

View File

@@ -0,0 +1,44 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <string>
#include <map>
#include <vector>
class Configuration
{
public:
Configuration();
virtual ~Configuration();
static void Initialize();
static void SetAbsolutePath(std::string absolutePath);
static std::string GetAbsolutePath();
static std::string ConvertToAbsolutePath(std::string prefix, std::string path);
// gets the global configuration
bool Import(std::string keyPrefix, std::string file);
bool GetProperty(std::string key, std::string &value);
bool GetProperty(std::string key, int &value);
bool GetProperty(std::string key, bool &value);
void GetChildKeyCrumbs(std::string parent, std::vector<std::string> &children);
void SetProperty(std::string key, std::string value);
bool PropertyExists(std::string key);
bool PropertyPrefixExists(std::string key);
bool GetPropertyAbsolutePath(std::string key, std::string &value);
bool IsVerbose() const;
void SetVerbose(bool verbose);
bool IsRequiredPropertiesSet();
private:
bool ParseLine(std::string keyPrefix, std::string line, int lineCount);
std::string TrimEnds(std::string str);
typedef std::map<std::string, std::string> PropertiesType;
typedef std::pair<std::string, std::string> PropertiesPair;
bool Verbose;
static std::string AbsolutePath;
PropertiesType Properties;
};

View File

@@ -0,0 +1,61 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DB.h"
#include "../Utility/Log.h"
#include <sstream>
#include <fstream>
DB::DB(std::string dbFile)
: Path(dbFile)
, Handle(NULL)
{
}
DB::~DB()
{
DeInitialize();
}
bool DB::Initialize()
{
bool retVal = false;
if(sqlite3_open(Path.c_str(), &Handle) != 0)
{
std::stringstream ss;
ss << "Cannot open database: \"" << Path << "\"" << sqlite3_errmsg(Handle);
Logger::Write(Logger::ZONE_ERROR, "Database", ss.str());
}
else
{
Logger::Write(Logger::ZONE_INFO, "Database", "Opened database \"" + Path + "\"");
retVal = true;
}
return retVal;
}
void DB::DeInitialize()
{
if(Handle != NULL)
{
sqlite3_close(Handle);
Handle = NULL;
}
}

View File

@@ -0,0 +1,24 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <sqlite3.h>
#include <string>
class DB
{
public:
DB(std::string dbFile);
bool Initialize();
void DeInitialize();
virtual ~DB();
sqlite3 *GetHandle()
{
return Handle;
}
private:
sqlite3 *Handle;
std::string Path;
};

View File

@@ -0,0 +1,132 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MamelistMetadata.h"
#include "DB.h"
#include "../Utility/Log.h"
#include "Metadata.h"
#include <rapidxml.hpp>
#include <fstream>
#include <sstream>
#include <vector>
#include <sqlite3.h>
MamelistMetadata::MamelistMetadata(DB &dbInstance)
: DBInstance(dbInstance)
{
}
MamelistMetadata::~MamelistMetadata()
{
}
bool MamelistMetadata::Import(std::string filename, std::string collection)
{
bool retVal = true;
rapidxml::xml_document<> doc;
rapidxml::xml_node<> * rootNode;
char *error = NULL;
sqlite3 *handle = DBInstance.GetHandle();
std::ifstream f(filename.c_str());
if (!f.good())
{
Logger::Write(Logger::ZONE_ERROR, "Mamelist", "Could not find mamelist metadata file at \"" + filename + "\"");
retVal = false;
}
f.close();
if(retVal)
{
Logger::Write(Logger::ZONE_INFO, "Mamelist", "Importing mamelist file \"" + filename + "\"");
std::ifstream file(filename.c_str());
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
buffer.push_back('\0');
doc.parse<0>(&buffer[0]);
rootNode = doc.first_node("mame");
sqlite3_exec(handle, "BEGIN IMMEDIATE TRANSACTION;", NULL, NULL, &error);
for (rapidxml::xml_node<> * game = rootNode->first_node("game"); game; game = game->next_sibling())
{
rapidxml::xml_attribute<> *nameNode = game->first_attribute("name");
rapidxml::xml_attribute<> *cloneOfXml = game->first_attribute("cloneof");
if(nameNode != NULL)
{
std::string name = nameNode->value();
rapidxml::xml_node<> *descriptionNode = game->first_node("description");
rapidxml::xml_node<> *yearNode = game->first_node("year");
rapidxml::xml_node<> *manufacturerNode = game->first_node("manufacturer");
rapidxml::xml_node<> *inputNode = game->first_node("input");
std::string description = (descriptionNode == NULL) ? nameNode->value() : descriptionNode->value();
std::string year = (yearNode == NULL) ? "" : yearNode->value();
std::string manufacturer = (manufacturerNode == NULL) ? "" : manufacturerNode->value();
std::string cloneOf = (cloneOfXml == NULL) ? "" : cloneOfXml->value();
std::string players;
std::string buttons;
if(inputNode != NULL)
{
rapidxml::xml_attribute<> *playersAttribute = inputNode->first_attribute("players");
rapidxml::xml_attribute<> *buttonsAttribute = inputNode->first_attribute("buttons");
if(playersAttribute)
{
players = playersAttribute->value();
}
if(buttonsAttribute)
{
buttons = buttonsAttribute->value();
}
}
sqlite3_stmt *stmt;
sqlite3_prepare_v2(handle,
"UPDATE OR REPLACE Meta SET title=?, year=?, manufacturer=?, players=?, buttons=?, cloneOf=? WHERE name=? AND collectionName=?;",
-1, &stmt, 0);
sqlite3_bind_text(stmt, 1, description.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, year.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, manufacturer.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 4, players.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 5, buttons.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 6, cloneOf.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 7, name.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 8, collection.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
}
sqlite3_exec(handle, "COMMIT TRANSACTION;", NULL, NULL, &error);
}
return retVal;
}

View File

@@ -0,0 +1,18 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Metadata.h"
class DB;
class MamelistMetadata : Metadata
{
public:
MamelistMetadata(DB &dbInstance);
virtual ~MamelistMetadata();
bool Import(std::string file, std::string collectionName);
private:
DB &DBInstance;
};

View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
class Metadata
{
public:
virtual ~Metadata() {}
virtual bool Import(std::string file, std::string collectionName) = 0;
};

View File

@@ -0,0 +1,61 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AttractMode.h"
#include "../Graphics/Page.h"
AttractMode::AttractMode()
: IsActive(false)
, ElapsedTime(0)
, ActiveTime(0)
, IdleTime(0)
{
}
void AttractMode::SetIdleTime(float time)
{
IdleTime = time;
}
void AttractMode::Reset()
{
ElapsedTime = 0;
IsActive = false;
ActiveTime = 0;
}
void AttractMode::Update(float dt, Page &page)
{
ElapsedTime += dt;
// enable attract mode when idling for the expected time. Disable if idle time is set to 0.
if(!IsActive && ElapsedTime > IdleTime && IdleTime > 0)
{
IsActive = true;
ElapsedTime = 0;
ActiveTime = ((float)((1000+rand()) % 5000)) / 1000;
}
if(IsActive)
{
page.SetScrolling(Page::ScrollDirectionForward);
if(ElapsedTime > ActiveTime)
{
ElapsedTime = 0;
IsActive = false;
page.SetScrolling(Page::ScrollDirectionIdle);
}
}
}

View File

@@ -0,0 +1,22 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
class Page;
class AttractMode
{
public:
AttractMode();
void SetIdleTime(float time);
void Reset();
void Update(float dt, Page &page);
private:
bool IsActive;
float ElapsedTime;
float ActiveTime;
float IdleTime;
};

View File

@@ -0,0 +1,339 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Launcher.h"
#include "../Collection/Item.h"
#include "../Utility/Log.h"
#include "../Database/Configuration.h"
#include "../Utility/Utils.h"
#include "../RetroFE.h"
#include "../SDL.h"
#include <cstdlib>
#include <locale>
#include <sstream>
#include <fstream>
#ifdef WIN32
#include <windows.h>
#include <cstring>
#endif
Launcher::Launcher(RetroFE &p, Configuration &c)
: Config(c)
, RetroFEInst(p)
{
}
bool Launcher::Run(std::string collection, Item *collectionItem)
{
std::string launcherName = collectionItem->GetLauncher();
std::string executablePath;
std::string selectedItemsDirectory;
std::string selectedItemsPath;
std::string currentDirectory;
std::string extensions;
std::string matchedExtension;
std::string args;
if(!GetLauncherExecutable(executablePath, currentDirectory, launcherName))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "Failed to find launcher executable (launcher: " + launcherName + " executable: " + executablePath + ")");
return false;
}
if(!GetExtensions(extensions, collection))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "No file extensions configured for collection \"" + collection + "\"");
return false;
}
if(!GetCollectionDirectory(selectedItemsDirectory, collection))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "Could not find files in directory \"" + selectedItemsDirectory + "\" for collection \"" + collection + "\"");
return false;
}
if(!GetLauncherArgs(args, launcherName))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "No launcher arguments specified for launcher " + launcherName);
return false;
}
if(!FindFile(selectedItemsPath, matchedExtension, selectedItemsDirectory, collectionItem->GetName(), extensions))
{
// FindFile() prints out diagnostic messages for us, no need to print anything here
return false;
}
args = ReplaceVariables(args,
selectedItemsPath,
collectionItem->GetName(),
collectionItem->GetFileName(),
selectedItemsDirectory,
collection);
executablePath = ReplaceVariables(executablePath,
selectedItemsPath,
collectionItem->GetName(),
collectionItem->GetFileName(),
selectedItemsDirectory,
collection);
currentDirectory = ReplaceVariables(currentDirectory,
selectedItemsPath,
collectionItem->GetName(),
collectionItem->GetFileName(),
selectedItemsDirectory,
collection);
if(!ExecuteCommand(executablePath, args, currentDirectory))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "Failed to launch.");
return false;
}
return true;
}
std::string Launcher::ReplaceVariables(std::string str,
std::string itemFilePath,
std::string itemName,
std::string itemFilename,
std::string itemDirectory,
std::string itemCollectionName)
{
str = Utils::Replace(str, "%ITEM_FILEPATH%", itemFilePath);
str = Utils::Replace(str, "%ITEM_NAME%", itemName);
str = Utils::Replace(str, "%ITEM_FILENAME%", itemFilename);
str = Utils::Replace(str, "%ITEM_DIRECTORY%", itemDirectory);
str = Utils::Replace(str, "%ITEM_COLLECTION_NAME%", itemCollectionName);
str = Utils::Replace(str, "%RETROFE_PATH%", Configuration::GetAbsolutePath());
#ifdef WIN32
str = Utils::Replace(str, "%RETROFE_EXEC_PATH%", Configuration::GetAbsolutePath() + "/RetroFE.exe");
#else
str = Utils::Replace(str, "%RETROFE_EXEC_PATH%", Configuration::GetAbsolutePath() + "/RetroFE");
#endif
return str;
}
bool Launcher::ExecuteCommand(std::string executable, std::string args, std::string currentDirectory)
{
bool retVal = false;
std::string executionString = "\"" + executable + "\" " + args;
Logger::Write(Logger::ZONE_INFO, "Launcher", "Attempting to launch: " + executionString);
Logger::Write(Logger::ZONE_INFO, "Launcher", " from within folder: " + currentDirectory);
//todo: use delegation instead of depending on knowing the RetroFE class (tie to an interface)
RetroFEInst.LaunchEnter();
#ifdef WIN32
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
char applicationName[256];
char currDir[256];
memset(&applicationName, 0, sizeof(applicationName));
memset(&startupInfo, 0, sizeof(startupInfo));
memset(&processInfo, 0, sizeof(processInfo));
strncpy(applicationName, executionString.c_str(), sizeof(applicationName));
strncpy(currDir, currentDirectory.c_str(), sizeof(currDir));
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.wShowWindow = SW_SHOWDEFAULT;
if(!CreateProcess(NULL, applicationName, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo))
#else
if(system(executionString.c_str()) != 0)
#endif
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "Failed to run: " + executable);
}
else
{
#ifdef WIN32
while(WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1, &processInfo.hProcess, FALSE, INFINITE, QS_ALLINPUT))
{
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
}
// result = GetExitCodeProcess(processInfo.hProcess, &exitCode);
CloseHandle(processInfo.hProcess);
#endif
retVal = true;
}
Logger::Write(Logger::ZONE_INFO, "Launcher", "Completed");
RetroFEInst.LaunchExit();
return retVal;
}
bool Launcher::GetLauncherName(std::string &launcherName, std::string collection)
{
std::string launcherKey = "collections." + collection + ".launcher";
// find the launcher for the particular item
if(!Config.GetProperty(launcherKey, launcherName))
{
std::stringstream ss;
ss << "Launch failed. Could not find a configured launcher for collection \""
<< collection
<< "\" (could not find a property for \""
<< launcherKey
<< "\")";
Logger::Write(Logger::ZONE_ERROR, "Launcher", ss.str());
return false;
}
std::stringstream ss;
ss << "collections."
<< collection
<< " is configured to use launchers."
<< launcherName
<< "\"";
Logger::Write(Logger::ZONE_DEBUG, "Launcher", ss.str());
return true;
}
bool Launcher::GetLauncherExecutable(std::string &executable, std::string &currentDirectory, std::string launcherName)
{
std::string executableKey = "launchers." + launcherName + ".executable";
if(!Config.GetProperty(executableKey, executable))
{
return false;
}
std::string currentDirectoryKey = "launchers." + launcherName + ".currentDirectory";
currentDirectory = Utils::GetDirectory(executable);
Config.GetProperty(currentDirectoryKey, currentDirectory);
return true;
}
bool Launcher::GetLauncherArgs(std::string &args, std::string launcherName)
{
std::string argsKey = "launchers." + launcherName + ".arguments";
if(!Config.GetProperty(argsKey, args))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "No arguments specified for: " + argsKey);
return false;
}
return true;
}
bool Launcher::GetExtensions(std::string &extensions, std::string collection)
{
std::string extensionsKey = "collections." + collection + ".list.extensions";
if(!Config.GetProperty(extensionsKey, extensions))
{
Logger::Write(Logger::ZONE_ERROR, "Launcher", "No extensions specified for: " + extensionsKey);
return false;
}
extensions = Utils::Replace(extensions, " ", "");
extensions = Utils::Replace(extensions, ".", "");
return true;
}
bool Launcher::GetCollectionDirectory(std::string &directory, std::string collection)
{
std::string itemsPathKey = "collections." + collection + ".list.path";
std::string itemsPathValue;
// find the items path folder (i.e. ROM path)
if(!Config.GetPropertyAbsolutePath(itemsPathKey, itemsPathValue))
{
directory = "";
}
else
{
directory += itemsPathValue + "/";
}
return true;
}
bool Launcher::FindFile(std::string &foundFilePath, std::string &foundFilename, std::string directory, std::string filenameWithoutExtension, std::string extensions)
{
std::string extension;
bool fileFound = false;
std::stringstream ss;
ss << extensions;
while(!fileFound && std::getline(ss, extension, ',') )
{
std::string selectedItemsPath = directory + filenameWithoutExtension + "." + extension;
std::ifstream f(selectedItemsPath.c_str());
if (f.good())
{
std::stringstream ss;
ss <<"Checking to see if \""
<< selectedItemsPath << "\" exists [Yes]";
fileFound = true;
Logger::Write(Logger::ZONE_INFO, "Launcher", ss.str());
foundFilePath = selectedItemsPath;
foundFilename = extension;
}
else
{
std::stringstream ss;
ss << "Checking to see if \""
<< selectedItemsPath << "\" exists [No]";
Logger::Write(Logger::ZONE_WARNING, "Launcher", ss.str());
}
f.close();
}
// get the launchers executable
if(!fileFound)
{
std::stringstream ss;
ss <<"Could not find any files with the name \""
<< filenameWithoutExtension << "\" in folder \""
<< directory;
Logger::Write(Logger::ZONE_ERROR, "Launcher", ss.str());
}
return fileFound;
}

View File

@@ -0,0 +1,40 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <string>
class Configuration;
class Item;
class RetroFE;
class Launcher
{
public:
Launcher(RetroFE &p, Configuration &c);
bool Run(std::string collection, Item *collectionItem);
private:
std::string ReplaceString(
std::string subject,
const std::string &search,
const std::string &replace);
bool GetLauncherName(std::string &launcherName, std::string collection);
bool GetLauncherExecutable(std::string &executable, std::string &currentDirectory, std::string launcherName);
bool GetLauncherArgs(std::string &args, std::string launcherName);
bool GetExtensions(std::string &extensions, std::string launcherName);
bool GetCollectionDirectory(std::string &directory, std::string collection);
bool ExecuteCommand(std::string executable, std::string arguments, std::string currentDirectory);
bool FindFile(std::string &foundFilePath, std::string &foundFilename, std::string directory, std::string filenameWithoutExtension, std::string extensions);
std::string ReplaceVariables(std::string str,
std::string itemFilePath,
std::string itemName,
std::string itemFilename,
std::string itemDirectory,
std::string itemCollectionName);
Configuration &Config;
RetroFE &RetroFEInst;
};

View File

@@ -0,0 +1,383 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Tween.h"
#include <algorithm>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string>
std::map<std::string, TweenAlgorithm> Tween::TweenTypeMap;
std::map<std::string, TweenProperty> Tween::TweenPropertyMap;
Tween::Tween(TweenProperty property, TweenAlgorithm type, double start, double end, double duration)
: Property(property)
, Type(type)
, Start(start)
, End(end)
, Duration(duration)
{
}
TweenProperty Tween::GetProperty() const
{
return Property;
}
bool Tween::GetTweenProperty(std::string name, TweenProperty &property)
{
bool retVal = false;
if(TweenPropertyMap.size() == 0)
{
TweenPropertyMap["x"] = TWEEN_PROPERTY_X;
TweenPropertyMap["y"] = TWEEN_PROPERTY_Y;
TweenPropertyMap["angle"] = TWEEN_PROPERTY_ANGLE;
TweenPropertyMap["alpha"] = TWEEN_PROPERTY_ALPHA;
TweenPropertyMap["width"] = TWEEN_PROPERTY_WIDTH;
TweenPropertyMap["height"] = TWEEN_PROPERTY_HEIGHT;
TweenPropertyMap["xorigin"] = TWEEN_PROPERTY_X_ORIGIN;
TweenPropertyMap["yorigin"] = TWEEN_PROPERTY_Y_ORIGIN;
TweenPropertyMap["xoffset"] = TWEEN_PROPERTY_X_OFFSET;
TweenPropertyMap["yoffset"] = TWEEN_PROPERTY_Y_OFFSET;
TweenPropertyMap["fontSize"] = TWEEN_PROPERTY_FONT_SIZE;
}
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
if(TweenPropertyMap.find(name) != TweenPropertyMap.end())
{
property = TweenPropertyMap[name];
retVal = true;
}
return retVal;
}
TweenAlgorithm Tween::GetTweenType(std::string name)
{
if(TweenTypeMap.size() == 0)
{
TweenTypeMap["easeinquadratic"] = EASE_IN_QUADRATIC;
TweenTypeMap["easeoutquadratic"] = EASE_OUT_QUADRATIC;
TweenTypeMap["easeinoutquadratic"] = EASE_INOUT_QUADRATIC;
TweenTypeMap["easeincubic"] = EASE_IN_CUBIC;
TweenTypeMap["easeoutcubic"] = EASE_OUT_CUBIC;
TweenTypeMap["easeinoutcubic"] = EASE_INOUT_CUBIC;
TweenTypeMap["easeinquartic"] = EASE_IN_QUARTIC;
TweenTypeMap["easeoutquartic"] = EASE_OUT_QUARTIC;
TweenTypeMap["easeinoutquartic"] = EASE_INOUT_QUARTIC;
TweenTypeMap["easeinquintic"] = EASE_IN_QUINTIC;
TweenTypeMap["easeoutquintic"] = EASE_OUT_QUINTIC;
TweenTypeMap["easeinoutquintic"] = EASE_INOUT_QUINTIC;
TweenTypeMap["easeinsine"] = EASE_IN_SINE;
TweenTypeMap["easeoutsine"] = EASE_OUT_SINE;
TweenTypeMap["easeinoutsine"] = EASE_INOUT_SINE;
TweenTypeMap["easeinexponential"] = EASE_IN_EXPONENTIAL;
TweenTypeMap["easeoutexponential"] = EASE_OUT_EXPONENTIAL;
TweenTypeMap["easeinoutexponential"] = EASE_INOUT_EXPONENTIAL;
TweenTypeMap["easeincircular"] = EASE_IN_CIRCULAR;
TweenTypeMap["easeoutcircular"] = EASE_OUT_CIRCULAR;
TweenTypeMap["easeinoutcircular"] = EASE_INOUT_CIRCULAR;
TweenTypeMap["linear"] = LINEAR;
}
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
if(TweenTypeMap.find(name) != TweenTypeMap.end())
{
return TweenTypeMap[name];
}
else
{
return TweenTypeMap["linear"];
}
}
float Tween::Animate(double elapsedTime)
{
return AnimateSingle(Type, Start, End, Duration, elapsedTime);
}
//todo: SDL likes floats, consider having casting being performed elsewhere
float Tween::AnimateSingle(TweenAlgorithm type, double start, double end, double duration, double elapsedTime)
{
double a = start;
double b = end - start;
double result = 0;
switch(type)
{
case EASE_IN_QUADRATIC:
result = EaseInQuadratic(elapsedTime, duration, a, b);
break;
case EASE_OUT_QUADRATIC:
result = EaseOutQuadratic(elapsedTime, duration, a, b);
break;
case EASE_INOUT_QUADRATIC:
result = EaseInOutQuadratic(elapsedTime, duration, a, b);
break;
case EASE_IN_CUBIC:
result = EaseInCubic(elapsedTime, duration, a, b);
break;
case EASE_OUT_CUBIC:
result = EaseOutCubic(elapsedTime, duration, a, b);
break;
case EASE_INOUT_CUBIC:
result = EaseInOutCubic(elapsedTime, duration, a, b);
break;
case EASE_IN_QUARTIC:
result = EaseInQuartic(elapsedTime, duration, a, b);
break;
case EASE_OUT_QUARTIC:
result = EaseOutQuartic(elapsedTime, duration, a, b);
break;
case EASE_INOUT_QUARTIC:
result = EaseInOutQuartic(elapsedTime, duration, a, b);
break;
case EASE_IN_QUINTIC:
result = EaseInQuintic(elapsedTime, duration, a, b);
break;
case EASE_OUT_QUINTIC:
result = EaseOutQuintic(elapsedTime, duration, a, b);
break;
case EASE_INOUT_QUINTIC:
result = EaseInOutQuintic(elapsedTime, duration, a, b);
break;
case EASE_IN_SINE:
result = EaseInSine(elapsedTime, duration, a, b);
break;
case EASE_OUT_SINE:
result = EaseOutSine(elapsedTime, duration, a, b);
break;
case EASE_INOUT_SINE:
result = EaseInOutSine(elapsedTime, duration, a, b);
break;
case EASE_IN_EXPONENTIAL:
result = EaseInExponential(elapsedTime, duration, a, b);
break;
case EASE_OUT_EXPONENTIAL:
result = EaseOutExponential(elapsedTime, duration, a, b);
break;
case EASE_INOUT_EXPONENTIAL:
result = EaseInOutExponential(elapsedTime, duration, a, b);
break;
case EASE_IN_CIRCULAR:
result = EaseInCircular(elapsedTime, duration, a, b);
break;
case EASE_OUT_CIRCULAR:
result = EaseOutCircular(elapsedTime, duration, a, b);
break;
case EASE_INOUT_CIRCULAR:
result = EaseInOutCircular(elapsedTime, duration, a, b);
break;
case LINEAR:
default:
result = Linear(elapsedTime, duration, a, b);
break;
}
return static_cast<float>(result);
}
double Tween::Linear(double t, double d, double b, double c)
{
if(d == 0) return b;
return c*t/d + b;
};
double Tween::EaseInQuadratic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
return c*t*t + b;
};
double Tween::EaseOutQuadratic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
return -c * t*(t-2) + b;
};
double Tween::EaseInOutQuadratic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
double Tween::EaseInCubic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
return c*t*t*t + b;
};
double Tween::EaseOutCubic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
t--;
return c*(t*t*t + 1) + b;
};
double Tween::EaseInOutCubic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d/2;
if (t < 1) return c/2*t*t*t + b;
t -= 2;
return c/2*(t*t*t + 2) + b;
};
double Tween::EaseInQuartic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
return c*t*t*t*t + b;
};
double Tween::EaseOutQuartic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
t--;
return -c * (t*t*t*t - 1) + b;
};
double Tween::EaseInOutQuartic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d/2;
if (t < 1) return c/2*t*t*t*t + b;
t -= 2;
return -c/2 * (t*t*t*t - 2) + b;
};
double Tween::EaseInQuintic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
return c*t*t*t*t*t + b;
};
double Tween::EaseOutQuintic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d;
t--;
return c*(t*t*t*t*t + 1) + b;
};
double Tween::EaseInOutQuintic(double t, double d, double b, double c)
{
if(d == 0) return b;
t /= d/2;
if (t < 1) return c/2*t*t*t*t*t + b;
t -= 2;
return c/2*(t*t*t*t*t + 2) + b;
};
double Tween::EaseInSine(double t, double d, double b, double c)
{
return -c * cos(t/d * (M_PI/2)) + c + b;
};
double Tween::EaseOutSine(double t, double d, double b, double c)
{
return c * sin(t/d * (M_PI/2)) + b;
};
double Tween::EaseInOutSine(double t, double d, double b, double c)
{
return -c/2 * (cos( M_PI*t/d) - 1) + b;
};
double Tween::EaseInExponential(double t, double d, double b, double c)
{
return c * pow( 2, 10 * (t/d - 1) ) + b;
};
double Tween::EaseOutExponential(double t, double d, double b, double c)
{
return c * ( - pow( 2, -10 * t/d ) + 1 ) + b;
};
double Tween::EaseInOutExponential(double t, double d, double b, double c)
{
t /= d/2;
if (t < 1) return c/2 * pow( 2, 10 * (t - 1) ) + b;
t--;
return c/2 * ( -1* pow( 2, -10 * t) + 2 ) + b;
};
double Tween::EaseInCircular(double t, double d, double b, double c)
{
t /= d;
return -c * (sqrt(1 - t*t) - 1) + b;
};
double Tween::EaseOutCircular(double t, double d, double b, double c)
{
t /= d;
t--;
return c * sqrt(1 - t*t) + b;
};
double Tween::EaseInOutCircular(double t, double d, double b, double c)
{
t /= d/2;
if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b;
t -= 2;
return c/2 * (sqrt(1 - t*t) + 1) + b;
}
;
//todo: sdl requires floats, should the casting be done at this layer?
float Tween::GetDuration() const
{
return static_cast<float>(Duration);
}

View File

@@ -0,0 +1,55 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "TweenTypes.h"
#include <string>
#include <map>
class ViewInfo;
class Tween
{
public:
Tween(TweenProperty name, TweenAlgorithm type, double start, double end, double duration);
float Animate(double elapsedTime);
static float AnimateSingle(TweenAlgorithm type, double start, double end, double duration, double elapsedTime);
static TweenAlgorithm GetTweenType(std::string name);
static bool GetTweenProperty(std::string name, TweenProperty &property);
TweenProperty GetProperty() const;
float GetDuration() const;
private:
static double EaseInQuadratic(double elapsedTime, double duration, double b, double c);
static double EaseOutQuadratic(double elapsedTime, double duration, double b, double c);
static double EaseInOutQuadratic(double elapsedTime, double duration, double b, double c);
static double EaseInCubic(double elapsedTime, double duration, double b, double c);
static double EaseOutCubic(double elapsedTime, double duration, double b, double c);
static double EaseInOutCubic(double elapsedTime, double duration, double b, double c);
static double EaseInQuartic(double elapsedTime, double duration, double b, double c);
static double EaseOutQuartic(double elapsedTime, double duration, double b, double c);
static double EaseInOutQuartic(double elapsedTime, double duration, double b, double c);
static double EaseInQuintic(double elapsedTime, double duration, double b, double c);
static double EaseOutQuintic(double elapsedTime, double duration, double b, double c);
static double EaseInOutQuintic(double elapsedTime, double duration, double b, double c);
static double EaseInSine(double elapsedTime, double duration, double b, double c);
static double EaseOutSine(double elapsedTime, double duration, double b, double c);
static double EaseInOutSine(double elapsedTime, double duration, double b, double c);
static double EaseInExponential(double elapsedTime, double duration, double b, double c);
static double EaseOutExponential(double elapsedTime, double duration, double b, double c);
static double EaseInOutExponential(double elapsedTime, double duration, double b, double c);
static double EaseInCircular(double elapsedTime, double duration, double b, double c);
static double EaseOutCircular(double elapsedTime, double duration, double b, double c);
static double EaseInOutCircular(double elapsedTime, double duration, double b, double c);
static double Linear(double elapsedTime, double duration, double b, double c);
static std::map<std::string, TweenAlgorithm> TweenTypeMap;
static std::map<std::string, TweenProperty> TweenPropertyMap;
TweenProperty Property;
TweenAlgorithm Type;
double Start;
double End;
double Duration;
};

View File

@@ -0,0 +1,45 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
enum TweenAlgorithm
{
LINEAR,
EASE_IN_QUADRATIC,
EASE_OUT_QUADRATIC,
EASE_INOUT_QUADRATIC,
EASE_IN_CUBIC,
EASE_OUT_CUBIC,
EASE_INOUT_CUBIC,
EASE_IN_QUARTIC,
EASE_OUT_QUARTIC,
EASE_INOUT_QUARTIC,
EASE_IN_QUINTIC,
EASE_OUT_QUINTIC,
EASE_INOUT_QUINTIC,
EASE_IN_SINE,
EASE_OUT_SINE,
EASE_INOUT_SINE,
EASE_IN_EXPONENTIAL,
EASE_OUT_EXPONENTIAL,
EASE_INOUT_EXPONENTIAL,
EASE_IN_CIRCULAR,
EASE_OUT_CIRCULAR,
EASE_INOUT_CIRCULAR,
};
enum TweenProperty
{
TWEEN_PROPERTY_HEIGHT,
TWEEN_PROPERTY_WIDTH,
TWEEN_PROPERTY_ANGLE,
TWEEN_PROPERTY_ALPHA,
TWEEN_PROPERTY_X,
TWEEN_PROPERTY_Y,
TWEEN_PROPERTY_X_ORIGIN,
TWEEN_PROPERTY_Y_ORIGIN,
TWEEN_PROPERTY_X_OFFSET,
TWEEN_PROPERTY_Y_OFFSET,
TWEEN_PROPERTY_FONT_SIZE
};

View File

@@ -0,0 +1,337 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Component.h"
#include "../Animate/Tween.h"
#include "../../Graphics/ViewInfo.h"
#include "../../Utility/Log.h"
#include "../../SDL.h"
Component::Component()
{
OnEnterTweens = NULL;
OnExitTweens = NULL;
OnIdleTweens = NULL;
OnHighlightEnterTweens = NULL;
OnHighlightExitTweens = NULL;
SelectedItem = NULL;
NewItemSelectedSinceEnter = false;
BackgroundTexture = NULL;
FreeGraphicsMemory();
}
Component::~Component()
{
FreeGraphicsMemory();
}
void Component::FreeGraphicsMemory()
{
CurrentAnimationState = HIDDEN;
EnterRequested = false;
ExitRequested = false;
NewItemSelected = false;
HighlightExitComplete = false;
CurrentTweens = NULL;
CurrentTweenIndex = 0;
CurrentTweenComplete = false;
ElapsedTweenTime = 0;
ScrollActive = false;
if(BackgroundTexture)
{
SDL_LockMutex(SDL::GetMutex());
SDL_DestroyTexture(BackgroundTexture);
SDL_UnlockMutex(SDL::GetMutex());
BackgroundTexture = NULL;
}
}
void Component::AllocateGraphicsMemory()
{
if(!BackgroundTexture)
{
// make a 4x4 pixel wide surface to be stretched during rendering, make it a white background so we can use
// color later
SDL_Surface *surface = SDL_CreateRGBSurface(0, 4, 4, 32, 0, 0, 0, 0);
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255));
SDL_LockMutex(SDL::GetMutex());
BackgroundTexture = SDL_CreateTextureFromSurface(SDL::GetRenderer(), surface);
SDL_UnlockMutex(SDL::GetMutex());
SDL_FreeSurface(surface);
SDL_SetTextureBlendMode(BackgroundTexture, SDL_BLENDMODE_BLEND);
}
}
void Component::TriggerEnterEvent()
{
EnterRequested = true;
}
void Component::TriggerExitEvent()
{
ExitRequested = true;
}
void Component::TriggerHighlightEvent(Item *selectedItem)
{
NewItemSelected = true;
this->SelectedItem = selectedItem;
}
bool Component::IsIdle()
{
return (CurrentAnimationState == IDLE);
}
bool Component::IsHidden()
{
return (CurrentAnimationState == HIDDEN);
}
bool Component::IsWaiting()
{
return (CurrentAnimationState == HIGHLIGHT_WAIT);
}
void Component::Update(float dt)
{
ElapsedTweenTime += dt;
HighlightExitComplete = false;
if(IsHidden() || IsWaiting() || (IsIdle() && ExitRequested))
{
CurrentTweenComplete = true;
}
if(CurrentTweenComplete)
{
CurrentTweens = NULL;
// There was no request to override our state path. Continue on as normal.
switch(CurrentAnimationState)
{
case ENTER:
CurrentTweens = OnHighlightEnterTweens;
CurrentAnimationState = HIGHLIGHT_ENTER;
break;
case EXIT:
CurrentTweens = NULL;
CurrentAnimationState = HIDDEN;
break;
case HIGHLIGHT_ENTER:
CurrentTweens = OnIdleTweens;
CurrentAnimationState = IDLE;
break;
case IDLE:
// prevent us from automatically jumping to the exit tween upon enter
if(EnterRequested)
{
EnterRequested = false;
NewItemSelected = false;
}
else if(IsScrollActive() || NewItemSelected || ExitRequested)
{
CurrentTweens = OnHighlightExitTweens;
CurrentAnimationState = HIGHLIGHT_EXIT;
}
else
{
CurrentTweens = OnIdleTweens;
CurrentAnimationState = IDLE;
}
break;
case HIGHLIGHT_EXIT:
// intentionally break down
case HIGHLIGHT_WAIT:
if(ExitRequested && (CurrentAnimationState == HIGHLIGHT_WAIT))
{
CurrentTweens = OnHighlightExitTweens;
CurrentAnimationState = HIGHLIGHT_EXIT;
}
else if(ExitRequested && (CurrentAnimationState == HIGHLIGHT_EXIT))
{
CurrentTweens = OnExitTweens;
CurrentAnimationState = EXIT;
ExitRequested = false;
}
else if(IsScrollActive())
{
CurrentTweens = NULL;
CurrentAnimationState = HIGHLIGHT_WAIT;
}
else if(NewItemSelected)
{
CurrentTweens = OnHighlightEnterTweens;
CurrentAnimationState = HIGHLIGHT_ENTER;
HighlightExitComplete = true;
NewItemSelected = false;
}
else
{
CurrentTweens = NULL;
CurrentAnimationState = HIGHLIGHT_WAIT;
}
break;
case HIDDEN:
if(EnterRequested || ExitRequested)
{
CurrentTweens = OnEnterTweens;
CurrentAnimationState = ENTER;
}
else
{
CurrentTweens = NULL;
CurrentAnimationState = HIDDEN;
}
}
CurrentTweenIndex = 0;
CurrentTweenComplete = false;
ElapsedTweenTime = 0;
}
CurrentTweenComplete = Animate(IsIdle());
}
void Component::Draw()
{
if(BackgroundTexture)
{
ViewInfo *info = GetBaseViewInfo();
SDL_Rect rect;
rect.h = static_cast<int>(info->GetHeight());
rect.w = static_cast<int>(info->GetWidth());
rect.x = static_cast<int>(info->GetXRelativeToOrigin());
rect.y = static_cast<int>(info->GetYRelativeToOrigin());
SDL_SetTextureColorMod(BackgroundTexture,
static_cast<char>(info->GetBackgroundRed()*255),
static_cast<char>(info->GetBackgroundGreen()*255),
static_cast<char>(info->GetBackgroundBlue()*255));
SDL::RenderCopy(BackgroundTexture, static_cast<char>(info->GetBackgroundAlpha()*255), NULL, &rect, info->GetAngle());
}
}
bool Component::Animate(bool loop)
{
bool completeDone = false;
if(!CurrentTweens || CurrentTweenIndex >= CurrentTweens->size())
{
completeDone = true;
}
else if(CurrentTweens)
{
bool currentDone = true;
std::vector<Tween *> *tweenSet = CurrentTweens->at(CurrentTweenIndex);
for(unsigned int i = 0; i < tweenSet->size(); i++)
{
Tween *tween = tweenSet->at(i);
float elapsedTime = ElapsedTweenTime;
//todo: too many levels of nesting
if(elapsedTime < tween->GetDuration())
{
currentDone = false;
}
else
{
elapsedTime = tween->GetDuration();
}
float value = tween->Animate(elapsedTime);
switch(tween->GetProperty())
{
case TWEEN_PROPERTY_X:
GetBaseViewInfo()->SetX(value);
break;
case TWEEN_PROPERTY_Y:
GetBaseViewInfo()->SetY(value);
break;
case TWEEN_PROPERTY_HEIGHT:
GetBaseViewInfo()->SetHeight(value);
break;
case TWEEN_PROPERTY_WIDTH:
GetBaseViewInfo()->SetWidth(value);
break;
case TWEEN_PROPERTY_ANGLE:
GetBaseViewInfo()->SetAngle(value);
break;
case TWEEN_PROPERTY_ALPHA:
GetBaseViewInfo()->SetAlpha(value);
break;
case TWEEN_PROPERTY_X_ORIGIN:
GetBaseViewInfo()->SetXOrigin(value);
break;
case TWEEN_PROPERTY_Y_ORIGIN:
GetBaseViewInfo()->SetYOrigin(value);
break;
case TWEEN_PROPERTY_X_OFFSET:
GetBaseViewInfo()->SetXOffset(value);
break;
case TWEEN_PROPERTY_Y_OFFSET:
GetBaseViewInfo()->SetYOffset(value);
break;
case TWEEN_PROPERTY_FONT_SIZE:
GetBaseViewInfo()->SetFontSize(value);
break;
}
}
if(currentDone)
{
CurrentTweenIndex++;
ElapsedTweenTime = 0;
}
}
if(!CurrentTweens || CurrentTweenIndex >= CurrentTweens->size())
{
if(loop)
{
CurrentTweenIndex = 0;
}
completeDone = true;
}
return completeDone;
}

View File

@@ -0,0 +1,125 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <vector>
#include "../../SDL.h"
#include "../MenuNotifierInterface.h"
#include "../ViewInfo.h"
#include "../Animate/Tween.h"
#include "../../Collection/Item.h"
class Component
{
public:
Component();
virtual ~Component();
virtual void FreeGraphicsMemory();
virtual void AllocateGraphicsMemory();
virtual void LaunchEnter() {}
virtual void LaunchExit() {}
void TriggerEnterEvent();
void TriggerExitEvent();
void TriggerHighlightEvent(Item *selectedItem);
bool IsIdle();
bool IsHidden();
bool IsWaiting();
typedef std::vector<std::vector<Tween *> *> TweenSets;
void SetOnEnterTweens(TweenSets *tweens)
{
this->OnEnterTweens = tweens;
}
void SetOnExitTweens(TweenSets *tweens)
{
this->OnExitTweens = tweens;
}
void SetOnIdleTweens(TweenSets *tweens)
{
this->OnIdleTweens = tweens;
}
void SetOnHighlightEnterTweens(TweenSets *tweens)
{
this->OnHighlightEnterTweens = tweens;
}
void SetOnHighlightExitTweens(TweenSets *tweens)
{
this->OnHighlightExitTweens = tweens;
}
virtual void Update(float dt);
virtual void Draw();
ViewInfo *GetBaseViewInfo()
{
return &BaseViewInfo;
}
void UpdateBaseViewInfo(ViewInfo &info)
{
BaseViewInfo = info;
}
bool IsScrollActive() const
{
return ScrollActive;
}
void SetScrollActive(bool scrollActive)
{
ScrollActive = scrollActive;
}
protected:
Item *GetSelectedItem()
{
return SelectedItem;
}
enum AnimationState
{
IDLE,
ENTER,
HIGHLIGHT_EXIT,
HIGHLIGHT_WAIT,
HIGHLIGHT_ENTER,
EXIT,
HIDDEN
};
AnimationState CurrentAnimationState;
bool EnterRequested;
bool ExitRequested;
bool NewItemSelected;
bool HighlightExitComplete;
bool NewItemSelectedSinceEnter;
private:
bool Animate(bool loop);
bool IsTweenSequencingComplete();
void ResetTweenSequence(std::vector<ViewInfo *> *tweens);
TweenSets *OnEnterTweens;
TweenSets *OnExitTweens;
TweenSets *OnIdleTweens;
TweenSets *OnHighlightEnterTweens;
TweenSets *OnHighlightExitTweens;
TweenSets *CurrentTweens;
unsigned int CurrentTweenIndex;
bool CurrentTweenComplete;
ViewInfo BaseViewInfo;
float ElapsedTweenTime;
Tween *TweenInst;
Item *SelectedItem;
bool ScrollActive;
SDL_Texture *BackgroundTexture;
};

View File

@@ -0,0 +1,44 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Container.h"
#include "../ViewInfo.h"
#include "../../SDL.h"
Container::Container()
{
AllocateGraphicsMemory();
}
Container::~Container()
{
FreeGraphicsMemory();
}
void Container::FreeGraphicsMemory()
{
Component::FreeGraphicsMemory();
}
void Container::AllocateGraphicsMemory()
{
Component::AllocateGraphicsMemory();
}
void Container::Draw()
{
Component::Draw();
}

View File

@@ -0,0 +1,18 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component.h"
#include <SDL2/SDL.h>
#include <string>
class Container : public Component
{
public:
Container();
virtual ~Container();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void Draw();
};

View File

@@ -0,0 +1,89 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Image.h"
#include "../ViewInfo.h"
#include "../../SDL.h"
#include "../../Utility/Log.h"
#include <SDL2/SDL_image.h>
Image::Image(std::string file, float scaleX, float scaleY)
: Texture(NULL)
, File(file)
, ScaleX(scaleX)
, ScaleY(scaleY)
{
AllocateGraphicsMemory();
}
Image::~Image()
{
FreeGraphicsMemory();
}
void Image::FreeGraphicsMemory()
{
Component::FreeGraphicsMemory();
SDL_LockMutex(SDL::GetMutex());
if (Texture != NULL)
{
SDL_DestroyTexture(Texture);
Texture = NULL;
}
SDL_UnlockMutex(SDL::GetMutex());
}
void Image::AllocateGraphicsMemory()
{
int width;
int height;
Component::AllocateGraphicsMemory();
if(!Texture)
{
SDL_LockMutex(SDL::GetMutex());
Texture = IMG_LoadTexture(SDL::GetRenderer(), File.c_str());
if (Texture != NULL)
{
SDL_SetTextureBlendMode(Texture, SDL_BLENDMODE_BLEND);
SDL_QueryTexture(Texture, NULL, NULL, &width, &height);
GetBaseViewInfo()->SetImageWidth(width * ScaleX);
GetBaseViewInfo()->SetImageHeight(height * ScaleY);
}
SDL_UnlockMutex(SDL::GetMutex());
}
}
void Image::Draw()
{
Component::Draw();
if(Texture)
{
ViewInfo *info = GetBaseViewInfo();
SDL_Rect rect;
rect.x = static_cast<int>(info->GetXRelativeToOrigin());
rect.y = static_cast<int>(info->GetYRelativeToOrigin());
rect.h = static_cast<int>(info->GetHeight());
rect.w = static_cast<int>(info->GetWidth());
SDL::RenderCopy(Texture, static_cast<char>((info->GetAlpha() * 255)), NULL, &rect, info->GetAngle());
}
}

View File

@@ -0,0 +1,24 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component.h"
#include <SDL2/SDL.h>
#include <string>
class Image : public Component
{
public:
Image(std::string file, float scaleX, float scaleY);
virtual ~Image();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void Draw();
protected:
SDL_Texture *Texture;
std::string File;
float ScaleX;
float ScaleY;
};

View File

@@ -0,0 +1,42 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ImageBuilder.h"
#include "../../Utility/Utils.h"
#include "../../Utility/Log.h"
#include <fstream>
Image * ImageBuilder::CreateImage(std::string path, std::string name, float scaleX, float scaleY)
{
Image *image = NULL;
std::vector<std::string> extensions;
extensions.push_back("png");
extensions.push_back("PNG");
extensions.push_back("jpg");
extensions.push_back("JPG");
extensions.push_back("jpeg");
extensions.push_back("JPEG");
std::string prefix = path + "/" + name;
std::string file;
if(Utils::FindMatchingFile(prefix, extensions, file))
{
image = new Image(file, scaleX, scaleY);
}
return image;
}

View File

@@ -0,0 +1,15 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Image.h"
#include "VideoComponent.h"
#include "../../Video/VideoFactory.h"
//todo: this is more of a factory than a builder
class ImageBuilder
{
public:
Image * CreateImage(std::string path, std::string name, float scaleX, float scaleY);
};

View File

@@ -0,0 +1,187 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ReloadableMedia.h"
#include "ImageBuilder.h"
#include "VideoBuilder.h"
#include "../ViewInfo.h"
#include "../../Video/VideoFactory.h"
#include "../../Database/Configuration.h"
#include "../../Utility/Log.h"
#include "../../Utility/Utils.h"
#include "../../SDL.h"
#include <fstream>
#include <vector>
#include <iostream>
ReloadableMedia::ReloadableMedia(std::string imagePath, std::string videoPath, bool isVideo, float scaleX, float scaleY)
: LoadedComponent(NULL)
, ImagePath(imagePath)
, VideoPath(videoPath)
, ReloadRequested(false)
, FirstLoad(true)
, IsVideo(isVideo)
, ScaleX(scaleX)
, ScaleY(scaleY)
{
AllocateGraphicsMemory();
}
ReloadableMedia::~ReloadableMedia()
{
if (LoadedComponent != NULL)
{
delete LoadedComponent;
}
}
void ReloadableMedia::Update(float dt)
{
if(NewItemSelected)
{
ReloadRequested = true;
}
// wait for the right moment to reload the image
if (ReloadRequested && (HighlightExitComplete || FirstLoad))
{
ReloadTexture();
ReloadRequested = false;
FirstLoad = false;
}
if(LoadedComponent)
{
LoadedComponent->Update(dt);
}
// needs to be ran at the end to prevent the NewItemSelected flag from being detected
Component::Update(dt);
}
void ReloadableMedia::AllocateGraphicsMemory()
{
FirstLoad = true;
if(LoadedComponent)
{
LoadedComponent->AllocateGraphicsMemory();
}
// NOTICE! needs to be done last to prevent flags from being missed
Component::AllocateGraphicsMemory();
}
void ReloadableMedia::LaunchEnter()
{
if(LoadedComponent)
{
LoadedComponent->LaunchEnter();
}
}
void ReloadableMedia::LaunchExit()
{
if(LoadedComponent)
{
LoadedComponent->LaunchExit();
}
}
void ReloadableMedia::FreeGraphicsMemory()
{
Component::FreeGraphicsMemory();
if(LoadedComponent)
{
LoadedComponent->FreeGraphicsMemory();
}
}
void ReloadableMedia::ReloadTexture()
{
bool found = false;
if(LoadedComponent)
{
delete LoadedComponent;
LoadedComponent = NULL;
}
Item *selectedItem = GetSelectedItem();
if (selectedItem != NULL)
{
if(IsVideo)
{
std::vector<std::string> names;
names.push_back(selectedItem->GetName());
if(selectedItem->GetCloneOf().length() > 0)
{
names.push_back(selectedItem->GetCloneOf());
}
for(unsigned int n = 0; n < names.size() && !found; ++n)
{
std::string filePrefix;
filePrefix.append(VideoPath);
filePrefix.append("/");
filePrefix.append(names[n]);
std::string file;
VideoBuilder videoBuild;
LoadedComponent = videoBuild.CreateVideo(VideoPath, names[n], ScaleX, ScaleY);
if(LoadedComponent)
{
LoadedComponent->AllocateGraphicsMemory();
found = true;
}
}
}
if(!LoadedComponent)
{
ImageBuilder imageBuild;
LoadedComponent = imageBuild.CreateImage(ImagePath, selectedItem->GetFullTitle(), ScaleX, ScaleY);
if (LoadedComponent != NULL)
{
LoadedComponent->AllocateGraphicsMemory();
GetBaseViewInfo()->SetImageWidth(LoadedComponent->GetBaseViewInfo()->GetImageWidth());
GetBaseViewInfo()->SetImageHeight(LoadedComponent->GetBaseViewInfo()->GetImageHeight());
}
}
}
}
void ReloadableMedia::Draw()
{
ViewInfo *info = GetBaseViewInfo();
Component::Draw();
if(LoadedComponent)
{
info->SetImageHeight(LoadedComponent->GetBaseViewInfo()->GetImageHeight());
info->SetImageWidth(LoadedComponent->GetBaseViewInfo()->GetImageWidth());
LoadedComponent->UpdateBaseViewInfo(*info);
LoadedComponent->Draw();
}
}

View File

@@ -0,0 +1,38 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component.h"
#include "../../Video/IVideo.h"
#include "../../Collection/Item.h"
#include <SDL2/SDL.h>
#include <string>
class Image;
//todo: this class should aggregate Image, Text, and Video component classes
class ReloadableMedia : public Component
{
public:
ReloadableMedia(std::string imagePath, std::string videoPath, bool isVideo, float scaleX, float scaleY);
virtual ~ReloadableMedia();
void Update(float dt);
void Draw();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void LaunchEnter();
void LaunchExit();
private:
void ReloadTexture();
Component *LoadedComponent;
std::string ImagePath;
std::string VideoPath;
bool ReloadRequested;
bool FirstLoad;
IVideo *VideoInst;
bool IsVideo;
float ScaleX;
float ScaleY;
};

View File

@@ -0,0 +1,170 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ReloadableText.h"
#include "../ViewInfo.h"
#include "../../Database/Configuration.h"
#include "../../Utility/Log.h"
#include "../../SDL.h"
#include <fstream>
#include <vector>
#include <iostream>
ReloadableText::ReloadableText(std::string type, Font *font, SDL_Color color, std::string layoutKey, std::string collection, float scaleX, float scaleY)
: ImageInst(NULL)
, LayoutKey(layoutKey)
, Collection(collection)
, ReloadRequested(false)
, FirstLoad(true)
, FontInst(font)
, FontColor(color)
, ScaleX(scaleX)
, ScaleY(scaleY)
{
Type = TextTypeUnknown;
if(type == "numberButtons")
{
Type = TextTypeNumberButtons;
}
else if(type == "numberPlayers")
{
Type = TextTypeNumberPlayers;
}
else if(type == "year")
{
Type = TextTypeYear;
}
else if(type == "title")
{
Type = TextTypeTitle;
}
else if(type == "manufacturer")
{
Type = TextTypeManufacturer;
}
AllocateGraphicsMemory();
}
ReloadableText::~ReloadableText()
{
if (ImageInst != NULL)
{
delete ImageInst;
}
}
void ReloadableText::Update(float dt)
{
if(NewItemSelected)
{
ReloadRequested = true;
}
// wait for the right moment to reload the image
if (ReloadRequested && (HighlightExitComplete || FirstLoad))
{
ReloadTexture();
ReloadRequested = false;
FirstLoad = false;
}
// needs to be ran at the end to prevent the NewItemSelected flag from being detected
Component::Update(dt);
}
void ReloadableText::AllocateGraphicsMemory()
{
FirstLoad = true;
ReloadTexture();
// NOTICE! needs to be done last to prevent flags from being missed
Component::AllocateGraphicsMemory();
}
void ReloadableText::LaunchEnter()
{
}
void ReloadableText::LaunchExit()
{
}
void ReloadableText::FreeGraphicsMemory()
{
Component::FreeGraphicsMemory();
if (ImageInst != NULL)
{
delete ImageInst;
ImageInst = NULL;
}
}
void ReloadableText::ReloadTexture()
{
if (ImageInst != NULL)
{
delete ImageInst;
ImageInst = NULL;
}
Item *selectedItem = GetSelectedItem();
if (selectedItem != NULL)
{
std::stringstream ss;
std::string text;
switch(Type)
{
case TextTypeNumberButtons:
ss << selectedItem->GetNumberButtons();
break;
case TextTypeNumberPlayers:
ss << selectedItem->GetNumberPlayers();
break;
case TextTypeYear:
ss << selectedItem->GetYear();
break;
case TextTypeTitle:
ss << selectedItem->GetTitle();
break;
case TextTypeManufacturer:
ss << selectedItem->GetManufacturer();
break;
default:
break;
}
ImageInst = new Text(ss.str(), FontInst, FontColor, ScaleX, ScaleY);
}
}
void ReloadableText::Draw()
{
ViewInfo *info = GetBaseViewInfo();
if(ImageInst)
{
ImageInst->UpdateBaseViewInfo(*info);
ImageInst->Draw();
}
}

View File

@@ -0,0 +1,48 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component.h"
#include "Text.h"
#include "../Font.h"
#include "../../Collection/Item.h"
#include <SDL2/SDL.h>
#include <string>
class ReloadableText : public Component
{
public:
ReloadableText(std::string type, Font *font, SDL_Color color, std::string layoutKey, std::string collectionName, float scaleX, float scaleY);
virtual ~ReloadableText();
void Update(float dt);
void Draw();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void LaunchEnter();
void LaunchExit();
private:
enum TextType
{
TextTypeUnknown = 0,
TextTypeNumberButtons,
TextTypeNumberPlayers,
TextTypeYear,
TextTypeTitle,
TextTypeManufacturer,
};
void ReloadTexture();
Text *ImageInst;
TextType Type;
std::string LayoutKey;
std::string Collection;
bool ReloadRequested;
bool FirstLoad;
Font *FontInst;
SDL_Color FontColor;
float ScaleX;
float ScaleY;
};

View File

@@ -0,0 +1,692 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../Animate/Tween.h"
#include "../Animate/TweenTypes.h"
#include "../ComponentItemBinding.h"
#include "../Font.h"
#include "ScrollingList.h"
#include "ImageBuilder.h"
#include "VideoBuilder.h"
#include "VideoComponent.h"
#include "ReloadableMedia.h"
#include "Text.h"
#include "../../Database/Configuration.h" // todo: decouple the GUI from the data
#include "../../Collection/Item.h"
#include "../../Utility/Log.h"
#include "../../SDL.h"
#include "../ViewInfo.h"
#include <math.h>
#include <SDL2/SDL_image.h>
#include <sstream>
//todo: remove coupling from configuration data (if possible)
ScrollingList::ScrollingList(Configuration &c,
float scaleX,
float scaleY,
Font *font,
SDL_Color fontColor,
std::string layoutKey,
std::string collectionName,
std::string imageType)
: IsScrollChangedStarted(true)
, IsScrollChangedSignalled(false)
, IsScrollChangedComplete(false)
, SpriteList(NULL)
, ScrollPoints(NULL)
, TweenEnterTime(0)
, FirstSpriteIndex(0)
, SelectedSpriteListIndex(0)
, CurrentAnimateTime(0) // in seconds
, ScrollTime(0) // in seconds
, CurrentScrollDirection(ScrollDirectionIdle)
, RequestedScrollDirection(ScrollDirectionIdle)
, CurrentScrollState(ScrollStateIdle)
, ScrollAcceleration(6) // todo: make configurable
, ScrollVelocity(0)
, Config(c)
, ScaleX(scaleX)
, ScaleY(scaleY)
, FontInst(font)
, FontColor(fontColor)
, LayoutKey(layoutKey)
, CollectionName(collectionName)
, ImageType(imageType)
, MaxLayer(0)
{
}
ScrollingList::~ScrollingList()
{
if(SpriteList)
{
std::vector<ComponentItemBinding *>::iterator it = SpriteList->begin();
while(it != SpriteList->end())
{
if(*it != NULL)
{
DeallocateTexture(*it);
if((*it)->GetCollectionItem())
{
delete (*it)->GetCollectionItem();
}
delete *it;
}
SpriteList->erase(it);
it = SpriteList->begin();
}
delete SpriteList;
SpriteList = NULL;
}
}
void ScrollingList::SetItems(std::vector<ComponentItemBinding *> *spriteList)
{
SpriteList = spriteList;
FirstSpriteIndex = 0;
// loop the scroll points if there are not enough
unsigned int originalSize = SpriteList->size();
while(ScrollPoints && ScrollPoints->size()+4 > SpriteList->size())
{
for(unsigned int i = 0; i < originalSize; ++i)
{
Item *newItem = new Item();
Item *originalItem = SpriteList->at(i)->GetCollectionItem();
*newItem = *originalItem;
ComponentItemBinding *newSprite = new ComponentItemBinding(newItem);
SpriteList->push_back(newSprite);
}
}
for(unsigned int i = 0; SpriteList && ScrollPoints && i < SelectedSpriteListIndex; ++i)
{
CircularDecrement(FirstSpriteIndex, SpriteList);
}
IsScrollChangedComplete = true;
}
void ScrollingList::SetPoints(std::vector<ViewInfo *> *scrollPoints)
{
ScrollPoints = scrollPoints;
for(unsigned int i = 0; i != scrollPoints->size(); ++i)
{
ViewInfo *info = scrollPoints->at(i);
MaxLayer = (MaxLayer < info->GetLayer()) ? MaxLayer : info->GetLayer();
}
}
void ScrollingList::SetSelectedIndex(int selectedIndex)
{
SelectedSpriteListIndex = selectedIndex;
for(unsigned int i = 0; SpriteList && ScrollPoints && i < SelectedSpriteListIndex; ++i)
{
CircularDecrement(FirstSpriteIndex, SpriteList);
}
}
void ScrollingList::Click()
{
if(CurrentScrollDirection == ScrollDirectionBack)
{
CircularDecrement(FirstSpriteIndex, SpriteList);
IsScrollChangedComplete = true;
}
if(CurrentScrollDirection == ScrollDirectionForward)
{
CircularIncrement(FirstSpriteIndex, SpriteList);
IsScrollChangedComplete = true;
}
}
unsigned int ScrollingList::GetNextTween(unsigned int currentIndex, std::vector<ViewInfo *> *list)
{
if(CurrentScrollDirection == ScrollDirectionForward)
{
CircularDecrement(currentIndex, list);
}
else if(CurrentScrollDirection == ScrollDirectionBack)
{
CircularIncrement(currentIndex, list);
}
return currentIndex;
}
void ScrollingList::PageUp()
{
if(ScrollPoints && ScrollPoints->size() > 4)
{
ScrollVelocity = 0;
unsigned int counts = ScrollPoints->size() - 4;
for(unsigned int i = 0; i < counts; i++)
{
CircularDecrement(FirstSpriteIndex, SpriteList);
}
}
CurrentScrollState = ScrollStatePageChange;
IsScrollChangedStarted = true;
IsScrollChangedSignalled = false;
IsScrollChangedComplete = false;
}
void ScrollingList::PageDown()
{
if(ScrollPoints && ScrollPoints->size() > 4)
{
unsigned int counts = ScrollPoints->size() - 4;
ScrollVelocity = 0;
for(unsigned int i = 0; i < counts; i++)
{
CircularIncrement(FirstSpriteIndex, SpriteList);
}
}
CurrentScrollState = ScrollStatePageChange;
IsScrollChangedStarted = true;
IsScrollChangedSignalled = false;
IsScrollChangedComplete = false;
}
void ScrollingList::FreeGraphicsMemory()
{
Component::FreeGraphicsMemory();
TweenEnterTime = 0;
CurrentAnimateTime = 0;
ScrollTime = 0;
CurrentScrollDirection = ScrollDirectionIdle;
RequestedScrollDirection = ScrollDirectionIdle;
CurrentScrollState = ScrollStateIdle;
ScrollAcceleration = 6; // todo: make configurable
ScrollVelocity = 0;
for(unsigned int i = 0; i < SpriteList->size(); i++)
{
ComponentItemBinding *s = SpriteList->at(i);
DeallocateTexture(s);
}
}
void ScrollingList::Update(float dt)
{
float scrollPeriod = 0;
Component::Update(dt);
if(!ScrollPoints)
{
return;
}
switch(CurrentScrollState)
{
case ScrollStateActive:
if(RequestedScrollDirection != CurrentScrollDirection)
{
CurrentScrollState = ScrollStateStopping;
}
break;
case ScrollStateIdle:
ScrollTime = 0;
CurrentAnimateTime = 0;
ScrollVelocity = 0;
if(RequestedScrollDirection != ScrollDirectionIdle)
{
CurrentScrollState = ScrollStateActive;
CurrentScrollDirection = RequestedScrollDirection;
}
break;
default:
break;
};
if(CurrentScrollState != ScrollStatePageChange && CurrentScrollState != ScrollStateIdle)
{
IsScrollChangedStarted = true;
ScrollTime += dt;
CurrentAnimateTime += dt;
ScrollVelocity = ScrollTime * ScrollAcceleration;
// clip at 5 items scrolled per second
if(ScrollVelocity > 30)
{
ScrollVelocity = 30;
}
if(ScrollVelocity > 0)
{
scrollPeriod = 1 / ScrollVelocity;
}
// have we exceeded the time of when to stop on the next item in the list?
if(CurrentScrollState == ScrollStateStopping && CurrentAnimateTime >= scrollPeriod)
{
Click();
CurrentAnimateTime = 0;
ScrollVelocity = 0;
CurrentScrollState = ScrollStateIdle;
}
}
while(CurrentScrollState != ScrollStatePageChange && ScrollVelocity > 0 && CurrentAnimateTime >= scrollPeriod)
{
Click();
CurrentAnimateTime -= scrollPeriod;
}
if(ScrollPoints && SpriteList->size() > 0 && FirstSpriteIndex < SpriteList->size())
{
unsigned int spriteIndex = FirstSpriteIndex;
unsigned int numIterations = (ScrollPoints->size() > SpriteList->size()) ? SpriteList->size() : ScrollPoints->size();
unsigned int start = (ScrollPoints->size() > SpriteList->size()) ? SelectedSpriteListIndex : 0;
for(unsigned int i = start; i < start+numIterations; i++)
{
ComponentItemBinding *s = SpriteList->at(spriteIndex);
unsigned int nextI = GetNextTween(i, ScrollPoints);
ViewInfo *currentViewInfo = ScrollPoints->at(i);
ViewInfo *nextViewInfo = ScrollPoints->at(nextI);
AllocateTexture(s);
Component *c = s->GetComponent();
if(c)
{
currentViewInfo->SetImageHeight(c->GetBaseViewInfo()->GetImageHeight());
currentViewInfo->SetImageWidth(c->GetBaseViewInfo()->GetImageWidth());
nextViewInfo->SetImageHeight(c->GetBaseViewInfo()->GetImageHeight());
nextViewInfo->SetImageWidth(c->GetBaseViewInfo()->GetImageWidth());
//todo: 30 is a magic number
ViewInfo *spriteViewInfo = c->GetBaseViewInfo();
spriteViewInfo->SetX(Tween::AnimateSingle(LINEAR, currentViewInfo->GetX(), nextViewInfo->GetX(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetY(Tween::AnimateSingle(LINEAR, currentViewInfo->GetY(), nextViewInfo->GetY(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetXOrigin(Tween::AnimateSingle(LINEAR, currentViewInfo->GetXOrigin(), nextViewInfo->GetXOrigin(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetYOrigin(Tween::AnimateSingle(LINEAR, currentViewInfo->GetYOrigin(), nextViewInfo->GetYOrigin(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetXOffset(Tween::AnimateSingle(LINEAR, currentViewInfo->GetXOffset(), nextViewInfo->GetXOffset(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetYOffset(Tween::AnimateSingle(LINEAR, currentViewInfo->GetYOffset(), nextViewInfo->GetYOffset(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetHeight(Tween::AnimateSingle(LINEAR, currentViewInfo->GetHeight(), nextViewInfo->GetHeight(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetWidth(Tween::AnimateSingle(LINEAR, currentViewInfo->GetWidth(), nextViewInfo->GetWidth(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetAlpha(Tween::AnimateSingle(LINEAR, currentViewInfo->GetAlpha(), nextViewInfo->GetAlpha(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetAngle(Tween::AnimateSingle(LINEAR, currentViewInfo->GetAngle(), nextViewInfo->GetAngle(), scrollPeriod, CurrentAnimateTime));
spriteViewInfo->SetFontSize(Tween::AnimateSingle(LINEAR, currentViewInfo->GetFontSize(), nextViewInfo->GetFontSize(), scrollPeriod, CurrentAnimateTime));
c->Update(dt);
}
CircularIncrement(spriteIndex, SpriteList);
}
// start freeing up memory if the list is too large
if(SpriteList->size() + 4 > ScrollPoints->size())
{
spriteIndex = FirstSpriteIndex;
CircularDecrement(spriteIndex, SpriteList);
DeallocateTexture(SpriteList->at(spriteIndex));
CircularDecrement(spriteIndex, SpriteList);
DeallocateTexture(SpriteList->at(spriteIndex));
// point to the end of the list to start deallocating..
// It's not fast, but it's easy to read
spriteIndex = FirstSpriteIndex;
for(unsigned int i = 0; i < ScrollPoints->size(); i++)
{
CircularIncrement(spriteIndex, SpriteList);
}
CircularIncrement(spriteIndex, SpriteList);
DeallocateTexture(SpriteList->at(spriteIndex));
CircularIncrement(spriteIndex, SpriteList);
DeallocateTexture(SpriteList->at(spriteIndex));
}
}
if(IsScrollChangedStarted && !IsScrollChangedSignalled)
{
IsScrollChangedSignalled = true;
ComponentItemBinding *sprite = GetPendingCollectionItemSprite();
Item *item = NULL;
if(sprite)
{
item = sprite->GetCollectionItem();
}
for(std::vector<MenuNotifierInterface *>::iterator it = NotificationComponents.begin();
it != NotificationComponents.end();
it++)
{
MenuNotifierInterface *c = *it;
if(c && item)
{
c->OnNewItemSelected(item);
}
}
if(CurrentScrollState == ScrollStatePageChange)
{
IsScrollChangedComplete = true;
CurrentScrollState = ScrollStateIdle;
}
}
if(IsScrollChangedStarted && IsScrollChangedSignalled && IsScrollChangedComplete)
{
IsScrollChangedStarted = false;
IsScrollChangedComplete = false;
IsScrollChangedSignalled = false;
}
}
void ScrollingList::AllocateTexture(ComponentItemBinding *s)
{
//todo: move this outside of the Draw routine
if(s && s->GetComponent() == NULL)
{
const Item *item = s->GetCollectionItem();
//todo: will create a runtime fault if not of the right type
//todo: remove coupling from knowing the collection name
std::string collectionKey ="collections." + CollectionName + ".media." + ImageType;
std::string videoKey ="collections." + CollectionName + ".media.video";
std::string imagePath;
std::string videoPath;
Component *t = NULL;
/*
// todo: to be supported at a later date
if(c->GetProperty(videoKey, videoPath))
{
t = new VideoComponent(videoPath, item->GetFullTitle(), ScaleX, ScaleY);
}
*/
if(!t && Config.GetPropertyAbsolutePath(collectionKey, imagePath))
{
ImageBuilder imageBuild;
t = imageBuild.CreateImage(imagePath, item->GetName(), ScaleX, ScaleY);
if(!t && item->GetTitle() != item->GetFullTitle())
{
t = imageBuild.CreateImage(imagePath, item->GetFullTitle(), ScaleX, ScaleY);
}
}
if (!t)
{
t = new Text(item->GetTitle(), FontInst, FontColor, ScaleX, ScaleY);
}
if(t)
{
s->SetComponent(t);
}
}
}
void ScrollingList::DeallocateTexture(ComponentItemBinding *s)
{
if(s && s->GetComponent() != NULL)
{
delete s->GetComponent();
}
s->SetComponent(NULL);
}
void ScrollingList::Draw()
{
//todo: Poor design implementation.
// caller should instead call ScrollingList::Draw(unsigned int layer)
}
//todo: this is kind of a hack. Aggregation needs to happen differently
void ScrollingList::Draw(unsigned int layer)
{
if(ScrollPoints && SpriteList && SpriteList->size() > 0 && FirstSpriteIndex < SpriteList->size())
{
unsigned int spriteIndex = FirstSpriteIndex;
for(unsigned int i = 0; i < ScrollPoints->size(); i++)
{
std::vector<ComponentItemBinding *>::iterator it = SpriteList->begin() + spriteIndex;
Component *c = (*it)->GetComponent();
ViewInfo *currentViewInfo = ScrollPoints->at(i);
if(currentViewInfo && currentViewInfo->GetLayer() == layer)
{
c->Draw();
}
CircularIncrement(spriteIndex, SpriteList);
}
}
}
void ScrollingList::SetScrollDirection(ScrollDirection direction)
{
RequestedScrollDirection = direction;
}
void ScrollingList::RemoveSelectedItem()
{
ComponentItemBinding *sprite = GetSelectedCollectionItemSprite();
if(sprite)
{
Item *item = sprite->GetCollectionItem();
DeallocateTexture(sprite);
int index = (FirstSpriteIndex + SelectedSpriteListIndex) % SpriteList->size();
std::vector<ComponentItemBinding *>::iterator it = SpriteList->begin() + index;
SpriteList->erase(it);
delete sprite;
if(item)
{
delete item;
}
if(SelectedSpriteListIndex >= SpriteList->size())
{
SelectedSpriteListIndex = 0;
}
if(FirstSpriteIndex >= SpriteList->size())
{
FirstSpriteIndex = 0;
}
}
IsScrollChangedComplete = true;
}
std::vector<ComponentItemBinding *> *ScrollingList::GetCollectionItemSprites()
{
return SpriteList;
}
ComponentItemBinding* ScrollingList::GetSelectedCollectionItemSprite()
{
ComponentItemBinding *item = NULL;
if(SpriteList && SpriteList->size() > 0)
{
int index = (FirstSpriteIndex + SelectedSpriteListIndex) % SpriteList->size();
item = SpriteList->at(index);
}
return item;
}
ComponentItemBinding* ScrollingList::GetPendingCollectionItemSprite()
{
ComponentItemBinding *item = NULL;
unsigned int index = FirstSpriteIndex;
if(CurrentScrollState != ScrollStatePageChange)
{
if(CurrentScrollDirection == ScrollDirectionBack)
{
CircularDecrement(index, SpriteList);
}
if(CurrentScrollDirection == ScrollDirectionForward)
{
CircularIncrement(index, SpriteList);
}
}
if(SpriteList && SpriteList->size() > 0)
{
index = (index + SelectedSpriteListIndex) % SpriteList->size();
item = SpriteList->at(index);
}
return item;
}
void ScrollingList::AddComponentForNotifications(MenuNotifierInterface *c)
{
NotificationComponents.push_back(c);
}
void ScrollingList::RemoveComponentForNotifications(MenuNotifierInterface *c)
{
for(std::vector<MenuNotifierInterface *>::iterator it = NotificationComponents.begin();
it != NotificationComponents.end();
it++)
{
if(c == *it)
{
NotificationComponents.erase(it);
break;
}
}
}
ComponentItemBinding* ScrollingList::GetPendingSelectedCollectionItemSprite()
{
ComponentItemBinding *item = NULL;
unsigned int index = SelectedSpriteListIndex;
if(CurrentScrollDirection == ScrollDirectionBack)
{
CircularDecrement(index, SpriteList);
}
if(CurrentScrollDirection == ScrollDirectionForward)
{
CircularIncrement(index, SpriteList);
}
if(SpriteList && SpriteList->size() > 0)
{
index = (index + SelectedSpriteListIndex) % SpriteList->size();
item = SpriteList->at(index);
}
return item;
}
bool ScrollingList::IsIdle()
{
return (Component::IsIdle() && CurrentScrollState == ScrollStateIdle);
}
void ScrollingList::CircularIncrement(unsigned int &index, std::vector<ViewInfo*>* list)
{
index++;
if(index >= list->size())
{
index = 0;
}
}
void ScrollingList::CircularDecrement(unsigned int &index, std::vector<ViewInfo*>* list)
{
if(index > 0)
{
index--;
}
else
{
if(list->size() > 0)
{
index = list->size() - 1;
}
else
{
index = 0;
}
}
}
void ScrollingList::CircularIncrement(unsigned int &index, std::vector<ComponentItemBinding*> *list)
{
index++;
if(index >= list->size())
{
index = 0;
}
}
void ScrollingList::CircularDecrement(unsigned int &index, std::vector<ComponentItemBinding*> *list)
{
if(index > 0)
{
index--;
}
else
{
if(list && list->size() > 0)
{
index = list->size() - 1;
}
else
{
index = 0;
}
}
}

View File

@@ -0,0 +1,105 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <vector>
#include "Component.h"
#include "../Animate/Tween.h"
#include "../ComponentItemBinding.h"
#include "../MenuNotifierInterface.h"
#include "../ViewInfo.h"
#include "../../Database/Configuration.h"
#include <SDL2/SDL.h>
//todo: This scrolling implementation needs to be overhauled
// It needs to have a common interface to support different menu types
// (It was originally sandbox code that creeped into here)
class Configuration;
class Font;
class ScrollingList : public Component
{
public:
enum ScrollDirection
{
ScrollDirectionBack,
ScrollDirectionForward,
ScrollDirectionIdle,
};
ScrollingList(Configuration &c, float scaleX, float scaleY, Font *font, SDL_Color fontColor, std::string layoutKey, std::string CollectionName, std::string imageType);
virtual ~ScrollingList();
void AllocateTexture(ComponentItemBinding *s);
void DeallocateTexture(ComponentItemBinding *s);
void SetItems(std::vector<ComponentItemBinding *> *spriteList);
void SetPoints(std::vector<ViewInfo *> *scrollPoints);
void SetScrollDirection(ScrollDirection direction);
void PageUp();
void PageDown();
bool IsIdle();
void SetSelectedIndex(int selectedIndex);
ComponentItemBinding *GetSelectedCollectionItemSprite();
ComponentItemBinding *GetPendingCollectionItemSprite();
ComponentItemBinding *GetPendingSelectedCollectionItemSprite();
void AddComponentForNotifications(MenuNotifierInterface *c);
void RemoveComponentForNotifications(MenuNotifierInterface *c);
std::vector<ComponentItemBinding *> *GetCollectionItemSprites();
void RemoveSelectedItem();
void FreeGraphicsMemory();
void Update(float dt);
void Draw();
void Draw(unsigned int layer);
private:
void Click();
unsigned int GetNextTween(unsigned int currentIndex, std::vector<ViewInfo *> *list);
bool IsScrollChangedStarted;
bool IsScrollChangedSignalled;
bool IsScrollChangedComplete;
enum ScrollState
{
ScrollStateActive,
ScrollStatePageChange,
ScrollStateStopping,
ScrollStateIdle
};
std::vector<ComponentItemBinding *> *SpriteList;
std::vector<ViewInfo *> *ScrollPoints;
std::vector<MenuNotifierInterface *> NotificationComponents;
float TweenEnterTime;
unsigned int FirstSpriteIndex;
unsigned int SelectedSpriteListIndex;
float CurrentAnimateTime;
float ScrollTime;
ScrollDirection CurrentScrollDirection;
ScrollDirection RequestedScrollDirection;
ScrollState CurrentScrollState;
float ScrollAcceleration;
float ScrollVelocity;
void CircularIncrement(unsigned &index, std::vector<ComponentItemBinding *> *list);
void CircularDecrement(unsigned &index, std::vector<ComponentItemBinding *> *list);
void CircularIncrement(unsigned &index, std::vector<ViewInfo *> *list);
void CircularDecrement(unsigned &index, std::vector<ViewInfo *> *list);
void UpdateOffset(float dt);
std::string Collection;
Configuration &Config;
float ScaleX;
float ScaleY;
Font *FontInst;
SDL_Color FontColor;
std::string LayoutKey;
std::string CollectionName;
std::string ImageType;
unsigned int MaxLayer;
};

View File

@@ -0,0 +1,112 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Text.h"
#include "../../Utility/Log.h"
#include "../../SDL.h"
#include "../Font.h"
#include <sstream>
Text::Text(std::string text, Font *font, SDL_Color fontColor, float scaleX, float scaleY)
: TextData(text)
, FontInst(font)
, FontColor(fontColor)
, ScaleX(scaleX)
, ScaleY(scaleY)
{
AllocateGraphicsMemory();
}
Text::~Text()
{
FreeGraphicsMemory();
}
void Text::FreeGraphicsMemory()
{
Component::FreeGraphicsMemory();
}
void Text::AllocateGraphicsMemory()
{
//todo: make the font blend color a parameter that is passed in
Component::AllocateGraphicsMemory();
}
void Text::Draw()
{
Component::Draw();
SDL_Texture *t = FontInst->GetTexture();
ViewInfo *info = GetBaseViewInfo();
float imageHeight = 0;
float imageWidth = 0;
// determine image width
for(unsigned int i = 0; i < TextData.size(); ++i)
{
Font::GlyphInfo glyph;
if(FontInst->GetRect(TextData[i], glyph))
{
imageWidth += glyph.Advance;
imageHeight = (imageHeight >= glyph.Rect.h) ? imageHeight : glyph.Rect.h;
}
}
float scale = (float)info->GetFontSize() / (float)imageHeight;
float width = info->GetRawWidth();
float height = info->GetRawHeight();
info->SetWidth(imageWidth*scale);
info->SetHeight(imageHeight*scale);
float xOrigin = info->GetXRelativeToOrigin();
float yOrigin = info->GetYRelativeToOrigin();
info->SetWidth(width);
info->SetHeight(height);
SDL_Rect rect;
rect.x = static_cast<int>(xOrigin);
for(unsigned int i = 0; i < TextData.size(); ++i)
{
Font::GlyphInfo glyph;
if(FontInst->GetRect(TextData[i], glyph) && glyph.Rect.h > 0)
{
SDL_Rect charRect = glyph.Rect;
float h = static_cast<float>(charRect.h * scale);
float w = static_cast<float>(charRect.w * scale);
rect.h = static_cast<int>(h);
rect.w = static_cast<int>(w);
rect.y = static_cast<int>(yOrigin);
SDL_LockMutex(SDL::GetMutex());
SDL_SetTextureColorMod(t, FontColor.r, FontColor.g, FontColor.b);
SDL_UnlockMutex(SDL::GetMutex());
SDL::RenderCopy(t, static_cast<char>(info->GetAlpha() * 255), &charRect, &rect, info->GetAngle());
rect.x += static_cast<int>(glyph.Advance * scale);
}
}
}

View File

@@ -0,0 +1,29 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component.h"
#include <SDL2/SDL.h>
#include <vector>
class Font;
class Text : public Component
{
public:
//todo: should have a Font flass that references fontcache, pass that in as an argument
Text(std::string text, Font *font, SDL_Color fontColor, float scaleX, float scaleY);
virtual ~Text();
void AllocateGraphicsMemory();
void FreeGraphicsMemory();
void Draw();
private:
std::string TextData;
Font *FontInst;
SDL_Color FontColor;
float ScaleX;
float ScaleY;
};

View File

@@ -0,0 +1,49 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "VideoBuilder.h"
#include "../../Utility/Utils.h"
#include "../../Utility/Log.h"
#include "../../Video/VideoFactory.h"
#include <fstream>
VideoComponent * VideoBuilder::CreateVideo(std::string path, std::string name, float scaleX, float scaleY)
{
VideoComponent *component = NULL;
std::vector<std::string> extensions;
extensions.push_back("mp4");
extensions.push_back("MP4");
extensions.push_back("avi");
extensions.push_back("AVI");
std::string prefix = path + "/" + name;
std::string file;
if(Utils::FindMatchingFile(prefix, extensions, file))
{
IVideo *video = Factory.CreateVideo();
if(video)
{
component = new VideoComponent(video, file, scaleX, scaleY);
}
}
return component;
}

View File

@@ -0,0 +1,18 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Image.h"
#include "VideoComponent.h"
#include "../../Video/VideoFactory.h"
//todo: this is more of a factory than a builder
class VideoBuilder
{
public:
VideoComponent * CreateVideo(std::string path, std::string name, float scaleX, float scaleY);
private:
VideoFactory Factory;
};

View File

@@ -0,0 +1,97 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "VideoComponent.h"
#include "../ViewInfo.h"
#include "../../Database/Configuration.h"
#include "../../Utility/Log.h"
#include "../../SDL.h"
VideoComponent::VideoComponent(IVideo *videoInst, std::string videoFile, float scaleX, float scaleY)
: VideoTexture(NULL)
, VideoFile(videoFile)
, VideoInst(videoInst)
, ScaleX(scaleX)
, ScaleY(scaleY)
, IsPlaying(false)
{
// AllocateGraphicsMemory();
}
VideoComponent::~VideoComponent()
{
FreeGraphicsMemory();
if(VideoInst)
{
VideoInst->Stop();
}
}
void VideoComponent::Update(float dt)
{
if(IsPlaying)
{
VideoInst->Update(dt);
}
Component::Update(dt);
}
void VideoComponent::AllocateGraphicsMemory()
{
Component::AllocateGraphicsMemory();
if(!IsPlaying)
{
IsPlaying = VideoInst->Play(VideoFile);
}
}
void VideoComponent::FreeGraphicsMemory()
{
VideoInst->Stop();
IsPlaying = false;
if (VideoTexture != NULL)
{
SDL_LockMutex(SDL::GetMutex());
SDL_DestroyTexture(VideoTexture);
SDL_UnlockMutex(SDL::GetMutex());
}
Component::FreeGraphicsMemory();
}
void VideoComponent::Draw()
{
ViewInfo *info = GetBaseViewInfo();
SDL_Rect rect;
rect.x = static_cast<int>(info->GetXRelativeToOrigin());
rect.y = static_cast<int>(info->GetYRelativeToOrigin());
rect.h = static_cast<int>(info->GetHeight());
rect.w = static_cast<int>(info->GetWidth());
VideoInst->Draw();
SDL_Texture *texture = VideoInst->GetTexture();
if(texture)
{
SDL::RenderCopy(texture, static_cast<int>(info->GetAlpha() * 255), NULL, &rect, info->GetAngle());
}
}

View File

@@ -0,0 +1,38 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component.h"
#include "Image.h"
#include "../../Collection/Item.h"
#include "../../Video/IVideo.h"
#include <SDL2/SDL.h>
#include <string>
class VideoComponent : public Component
{
public:
VideoComponent(IVideo *videoInst, std::string videoFile, float scaleX, float scaleY);
virtual ~VideoComponent();
void Update(float dt);
void Draw();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void LaunchEnter()
{
FreeGraphicsMemory();
}
void LaunchExit()
{
AllocateGraphicsMemory();
}
private:
SDL_Texture *VideoTexture;
std::string VideoFile;
std::string Name;
IVideo *VideoInst;
float ScaleX;
float ScaleY;
bool IsPlaying;
};

View File

@@ -0,0 +1,47 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ComponentItemBinding.h"
ComponentItemBinding::ComponentItemBinding( Component *c, Item *item)
: CollectionComponent(c)
, CollectionItem(item)
{
}
ComponentItemBinding::ComponentItemBinding(Item *item)
: CollectionComponent(NULL)
, CollectionItem(item)
{
}
ComponentItemBinding::~ComponentItemBinding()
{
}
Item* ComponentItemBinding::GetCollectionItem() const
{
return CollectionItem;
}
void ComponentItemBinding::SetComponent(Component *c)
{
CollectionComponent = c;
}
Component* ComponentItemBinding::GetComponent() const
{
return CollectionComponent;
}

View File

@@ -0,0 +1,23 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component/Component.h"
#include "../Collection/Item.h"
class ComponentItemBinding
{
public:
ComponentItemBinding(Component *c, Item *item);
ComponentItemBinding(Item *item);
virtual ~ComponentItemBinding();
Item* GetCollectionItem() const;
void SetComponent(Component *c);
Component* GetComponent() const;
private:
Component *CollectionComponent;
Item *CollectionItem;
};

View File

@@ -0,0 +1,41 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ComponentItemBindingBuilder.h"
#include "ComponentItemBinding.h"
#include "../Database/CollectionDatabase.h"
#include "../Collection/Item.h"
ComponentItemBindingBuilder::ComponentItemBindingBuilder()
{
}
ComponentItemBindingBuilder::~ComponentItemBindingBuilder()
{
}
std::vector<ComponentItemBinding *> *ComponentItemBindingBuilder::BuildCollectionItems(std::vector<Item *> *infoList)
{
std::vector<ComponentItemBinding *> *sprites = new std::vector<ComponentItemBinding *>();
std::vector<Item *>::iterator it;
for(it = infoList->begin(); it != infoList->end(); ++it)
{
ComponentItemBinding *s = new ComponentItemBinding(*it);
sprites->push_back(s);
}
return sprites;
}

View File

@@ -0,0 +1,18 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <vector>
#include <string>
class Item;
class ComponentItemBinding;
class ComponentItemBindingBuilder
{
public:
ComponentItemBindingBuilder();
virtual ~ComponentItemBindingBuilder();
static std::vector<ComponentItemBinding *> *BuildCollectionItems(std::vector<Item *> *infoList);
};

View File

@@ -0,0 +1,157 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Font.h"
#include "../SDL.h"
#include "../Utility/Log.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
Font::Font()
: Texture(NULL)
{
}
Font::~Font()
{
DeInitialize();
}
SDL_Texture *Font::GetTexture()
{
return Texture;
}
bool Font::GetRect(unsigned int charCode, GlyphInfo &glyph)
{
std::map<unsigned int, GlyphInfoBuild *>::iterator it = Atlas.find(charCode);
if(it != Atlas.end())
{
GlyphInfoBuild *info = it->second;
glyph = info->Glyph;
return true;
}
return false;
}
bool Font::Initialize(std::string fontPath, SDL_Color color)
{
TTF_Font *font = TTF_OpenFont(fontPath.c_str(), 128);
if (!font)
{
Logger::Write(Logger::ZONE_ERROR, "FontCache", "TTF_OpenFont failed");
return false;
}
int x = 0;
int y = 0;
int atlasHeight = 0;
int atlasWidth = 0;
for(unsigned short int i = 32; i < 128; ++i)
{
GlyphInfoBuild *info = new GlyphInfoBuild;
memset(info, sizeof(GlyphInfoBuild), 0);
info->Surface = TTF_RenderGlyph_Blended(font, i, color);
TTF_GlyphMetrics(font, i, &info->Glyph.MinX, &info->Glyph.MaxX, &info->Glyph.MinY, &info->Glyph.MaxY, &info->Glyph.Advance);
if(x + info->Surface->w >= 1024)
{
atlasHeight += y;
atlasWidth = (atlasWidth >= x) ? atlasWidth : x;
x = 0;
y = 0;
}
info->Glyph.Rect.w = info->Surface->w;
info->Glyph.Rect.h = info->Surface->h;
info->Glyph.Rect.x = x;
info->Glyph.Rect.y = atlasHeight;
Atlas[i] = info;
x += info->Glyph.Rect.w;
y = (y > info->Glyph.Rect.h) ? y : info->Glyph.Rect.h;
/*
std::stringstream ss;
ss << " tw:" << atlasWidth << " th:" << atlasHeight << " x:" << x << " y:" << y << " w:" << info->Glyph.Rect.w << " h:" << info->Glyph.Rect.h;
Logger::Write(Logger::ZONE_ERROR, "FontCache", ss.str());
*/
}
atlasWidth = (atlasWidth >= x) ? atlasWidth : x;
atlasHeight += y;
unsigned int rmask;
unsigned int gmask;
unsigned int bmask;
unsigned int amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
SDL_Surface *atlasSurface = SDL_CreateRGBSurface(0, atlasWidth, atlasHeight, 24, rmask, gmask, bmask, amask);
std::map<unsigned int, GlyphInfoBuild *>::iterator it;
for(it = Atlas.begin(); it != Atlas.end(); it++)
{
GlyphInfoBuild *info = it->second;
SDL_BlitSurface(info->Surface, NULL, atlasSurface, &info->Glyph.Rect);
SDL_FreeSurface(info->Surface);
info->Surface = NULL;
}
SDL_LockMutex(SDL::GetMutex());
SDL_SetColorKey(atlasSurface, SDL_TRUE, SDL_MapRGB(atlasSurface->format, 0, 0, 0));
Texture = SDL_CreateTextureFromSurface(SDL::GetRenderer(), atlasSurface);
SDL_FreeSurface(atlasSurface);
SDL_UnlockMutex(SDL::GetMutex());
TTF_CloseFont(font);
return true;
}
void Font::DeInitialize()
{
if(Texture)
{
SDL_LockMutex(SDL::GetMutex());
SDL_DestroyTexture(Texture);
Texture = NULL;
SDL_UnlockMutex(SDL::GetMutex());
}
std::map<unsigned int, GlyphInfoBuild *>::iterator atlasIt = Atlas.begin();
while(atlasIt != Atlas.end())
{
delete atlasIt->second;
Atlas.erase(atlasIt);
atlasIt = Atlas.begin();
}
}

View File

@@ -0,0 +1,42 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include <SDL2/SDL.h>
#include <map>
#include <string>
class Font
{
public:
struct GlyphInfo
{
int MinX;
int MaxX;
int MinY;
int MaxY;
int Advance;
SDL_Rect Rect;
};
Font();
virtual ~Font();
bool Initialize(std::string fontPath, SDL_Color color);
void DeInitialize();
SDL_Texture *GetTexture();
bool GetRect(unsigned int charCode, GlyphInfo &glyph);
private:
struct GlyphInfoBuild
{
Font::GlyphInfo Glyph;
SDL_Surface *Surface;
};
std::map<unsigned int, GlyphInfoBuild *> Atlas;
SDL_Texture *Texture;
};

View File

@@ -0,0 +1,82 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FontCache.h"
#include "Font.h"
#include "../Utility/Log.h"
#include "../SDL.h"
#include <SDL2/SDL_ttf.h>
#include <sstream>
//todo: memory leak when launching games
FontCache::FontCache()
{
}
FontCache::~FontCache()
{
DeInitialize();
}
void FontCache::DeInitialize()
{
std::map<std::string, Font *>::iterator it = FontFaceMap.begin();
while(it != FontFaceMap.end())
{
delete it->second;
FontFaceMap.erase(it);
it = FontFaceMap.begin();
}
SDL_LockMutex(SDL::GetMutex());
TTF_Quit();
SDL_UnlockMutex(SDL::GetMutex());
}
void FontCache::Initialize()
{
//todo: make bool
TTF_Init();
}
Font *FontCache::GetFont(std::string fontPath)
{
Font *t = NULL;
std::map<std::string, Font *>::iterator it = FontFaceMap.find(fontPath);
if(it != FontFaceMap.end())
{
t = it->second;
}
return t;
}
bool FontCache::LoadFont(std::string fontPath, SDL_Color color)
{
std::map<std::string, Font *>::iterator it = FontFaceMap.find(fontPath);
if(it == FontFaceMap.end())
{
Font *f = new Font();
f->Initialize(fontPath, color);
FontFaceMap[fontPath] = f;
}
return true;
}

View File

@@ -0,0 +1,24 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Font.h"
#include <string>
#include <map>
class FontCache
{
public:
void Initialize();
void DeInitialize();
FontCache();
bool LoadFont(std::string font, SDL_Color color);
Font *GetFont(std::string font);
virtual ~FontCache();
private:
std::map<std::string, Font *> FontFaceMap;
};

View File

@@ -0,0 +1,14 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "../Collection/Item.h"
class MenuNotifierInterface
{
public:
virtual ~MenuNotifierInterface() {}
virtual void OnNewItemSelected(Item *) = 0;
};

View File

@@ -0,0 +1,394 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Page.h"
#include "ComponentItemBinding.h"
#include "Component/Component.h"
#include "../Utility/Log.h"
#include "Component/ScrollingList.h"
#include "../Sound/Sound.h"
#include "ComponentItemBindingBuilder.h"
#include <sstream>
Page::Page(std::string collectionName)
: CollectionName(collectionName)
, Menu(NULL)
, Items(NULL)
, ScrollActive(false)
, SelectedItem(NULL)
, SelectedItemChanged(false)
, LoadSoundChunk(NULL)
, UnloadSoundChunk(NULL)
, HighlightSoundChunk(NULL)
, SelectSoundChunk(NULL)
, HasSoundedWhenActive(false)
, FirstSoundPlayed(false)
{
}
Page::~Page()
{
if(Menu)
{
Menu->RemoveComponentForNotifications(this);
}
for(unsigned int i = 0; i < sizeof(LayerComponents)/sizeof(LayerComponents[0]); ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
delete *it;
}
LayerComponents[i].clear();
}
if(Menu)
{
delete Menu;
}
if(LoadSoundChunk)
{
delete LoadSoundChunk;
LoadSoundChunk = NULL;
}
if(UnloadSoundChunk)
{
delete UnloadSoundChunk;
UnloadSoundChunk = NULL;
}
if(HighlightSoundChunk)
{
delete HighlightSoundChunk;
HighlightSoundChunk = NULL;
}
if(SelectSoundChunk)
{
delete SelectSoundChunk;
SelectSoundChunk = NULL;
}
}
void Page::OnNewItemSelected(Item *item)
{
SelectedItem = item;
SelectedItemChanged = true;
}
void Page::SetMenu(ScrollingList *s)
{
// todo: delete the old menu
Menu = s;
Menu->AddComponentForNotifications(this);
}
bool Page::AddComponent(Component *c)
{
bool retVal = false;
unsigned int layer = c->GetBaseViewInfo()->GetLayer();
if(layer < NUM_LAYERS)
{
LayerComponents[layer].push_back(c);
retVal = true;
}
else
{
std::stringstream ss;
ss << "Component layer too large Layer: " << layer;
Logger::Write(Logger::ZONE_ERROR, "Page", ss.str());
}
return retVal;
}
bool Page::IsIdle()
{
bool idle = true;
if(!Menu->IsIdle())
{
idle = false;
}
for(unsigned int i = 0; i < NUM_LAYERS && idle; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end() && idle; ++it)
{
idle = (*it)->IsIdle();
}
}
return idle;
}
bool Page::IsHidden()
{
bool hidden = Menu->IsHidden();
for(unsigned int i = 0; hidden && i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); hidden && it != LayerComponents[i].end(); ++it)
{
hidden = (*it)->IsHidden();
}
}
return hidden;
}
void Page::Start()
{
Menu->TriggerEnterEvent();
if(LoadSoundChunk)
{
LoadSoundChunk->Play();
}
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->TriggerEnterEvent();
}
}
}
void Page::Stop()
{
Menu->TriggerExitEvent();
if(UnloadSoundChunk)
{
UnloadSoundChunk->Play();
}
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->TriggerExitEvent();
}
}
}
Item *Page::GetSelectedItem()
{
return SelectedItem;
}
void Page::RemoveSelectedItem()
{
//todo: change method to RemoveItem() and pass in SelectedItem
Menu->RemoveSelectedItem();
SelectedItem = NULL;
}
void Page::Highlight()
{
Item *item = SelectedItem;
if(item)
{
Menu->TriggerHighlightEvent(item);
Menu->SetScrollActive(ScrollActive);
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->TriggerHighlightEvent(item);
(*it)->SetScrollActive(ScrollActive);
}
}
}
}
void Page::SetScrolling(ScrollDirection direction)
{
ScrollingList::ScrollDirection menuDirection;
switch(direction)
{
case ScrollDirectionForward:
menuDirection = ScrollingList::ScrollDirectionForward;
ScrollActive = true;
break;
case ScrollDirectionBack:
menuDirection = ScrollingList::ScrollDirectionBack;
ScrollActive = true;
break;
case ScrollDirectionIdle:
default:
menuDirection = ScrollingList::ScrollDirectionIdle;
ScrollActive = false;
break;
}
Menu->SetScrollDirection(menuDirection);
}
void Page::PageScroll(ScrollDirection direction)
{
if(direction == ScrollDirectionForward)
{
Menu->PageDown();
}
if(direction == ScrollDirectionBack)
{
Menu->PageUp();
}
}
void Page::SetItems(std::vector<Item *> *items)
{
std::vector<ComponentItemBinding *> *sprites = ComponentItemBindingBuilder::BuildCollectionItems(items);
Menu->SetItems(sprites);
}
void Page::Update(float dt)
{
Menu->Update(dt);
if(SelectedItemChanged && !HasSoundedWhenActive && HighlightSoundChunk)
{
// skip the first sound being played (as it is part of the on-enter)
if(FirstSoundPlayed)
{
HighlightSoundChunk->Play();
HasSoundedWhenActive = true;
}
FirstSoundPlayed = true;
}
if(SelectedItemChanged && !ScrollActive)
{
Highlight();
SelectedItemChanged = false;
HasSoundedWhenActive = false;
}
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->Update(dt);
}
}
}
void Page::Draw()
{
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->Draw();
}
Menu->Draw(i);
}
}
const std::string& Page::GetCollectionName() const
{
return CollectionName;
}
void Page::FreeGraphicsMemory()
{
Logger::Write(Logger::ZONE_DEBUG, "Page", "Free");
Menu->FreeGraphicsMemory();
if(LoadSoundChunk) LoadSoundChunk->Free();
if(UnloadSoundChunk) UnloadSoundChunk->Free();
if(HighlightSoundChunk) HighlightSoundChunk->Free();
if(SelectSoundChunk) SelectSoundChunk->Free();
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->FreeGraphicsMemory();
}
}
}
void Page::AllocateGraphicsMemory()
{
FirstSoundPlayed = false;
Logger::Write(Logger::ZONE_DEBUG, "Page", "Allocating graphics memory");
Menu->AllocateGraphicsMemory();
if(LoadSoundChunk) LoadSoundChunk->Allocate();
if(UnloadSoundChunk) UnloadSoundChunk->Allocate();
if(HighlightSoundChunk) HighlightSoundChunk->Allocate();
if(SelectSoundChunk) SelectSoundChunk->Allocate();
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->AllocateGraphicsMemory();
}
}
Logger::Write(Logger::ZONE_DEBUG, "Page", "Allocate graphics memory complete");
}
void Page::LaunchEnter()
{
Menu->LaunchEnter();
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->LaunchEnter();
}
}
}
void Page::LaunchExit()
{
Menu->LaunchExit();
for(unsigned int i = 0; i < NUM_LAYERS; ++i)
{
for(std::vector<Component *>::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it)
{
(*it)->LaunchExit();
}
}
}

View File

@@ -0,0 +1,83 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "MenuNotifierInterface.h"
#include <vector>
#include <string>
class Component;
class ScrollingList;
class Item;
class Sound;
class Page : public MenuNotifierInterface
{
public:
enum ScrollDirection
{
ScrollDirectionForward,
ScrollDirectionBack,
ScrollDirectionIdle
};
Page(std::string collectionName);
virtual ~Page();
virtual void OnNewItemSelected(Item *);
void SetItems(std::vector<Item *> *items);
void SetMenu(ScrollingList *s);
void SetLoadSound(Sound *chunk)
{
LoadSoundChunk = chunk;
}
void SetUnloadSound(Sound *chunk)
{
UnloadSoundChunk = chunk;
}
void SetHighlightSound(Sound *chunk)
{
HighlightSoundChunk = chunk;
}
void SetSelectSound(Sound *chunk)
{
SelectSoundChunk = chunk;
}
bool AddComponent(Component *c);
void PageScroll(ScrollDirection direction);
void Start();
void Stop();
void SetScrolling(ScrollDirection direction);
Item *GetSelectedItem();
Item *GetPendingSelectedItem();
void RemoveSelectedItem();
bool IsIdle();
bool IsHidden();
void Update(float dt);
void Draw();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void LaunchEnter();
void LaunchExit();
const std::string& GetCollectionName() const;
private:
void Highlight();
std::string CollectionName;
ScrollingList *Menu;
static const unsigned int NUM_LAYERS = 8;
std::vector<Component *> LayerComponents[NUM_LAYERS];
std::vector<Item *> *Items;
bool ScrollActive;
Item *SelectedItem;
bool SelectedItemChanged;
Sound *LoadSoundChunk;
Sound *UnloadSoundChunk;
Sound *HighlightSoundChunk;
Sound *SelectSoundChunk;
bool HasSoundedWhenActive;
bool FirstSoundPlayed;
};

View File

@@ -0,0 +1,876 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PageBuilder.h"
#include "Page.h"
#include "ViewInfo.h"
#include "Component/Container.h"
#include "Component/Image.h"
#include "Component/Text.h"
#include "Component/ReloadableText.h"
#include "Component/ReloadableMedia.h"
#include "Component/ScrollingList.h"
#include "Animate/TweenTypes.h"
#include "../Sound/Sound.h"
#include "../Collection/Item.h"
#include "../SDL.h"
#include "../Utility/Log.h"
#include "../Utility/Utils.h"
#include <algorithm>
#include <cfloat>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <map>
using namespace rapidxml;
//todo: this file is starting to become a god class of building. Consider splitting into sub-builders
PageBuilder::PageBuilder(std::string layoutKey, std::string collection, Configuration &c, FontCache *fc)
: LayoutKey(layoutKey)
, Collection(collection)
, Config(c)
, ScaleX(1)
, ScaleY(1)
, ScreenHeight(0)
, ScreenWidth(0)
, FC(fc)
{
ScreenWidth = SDL::GetWindowWidth();
ScreenHeight = SDL::GetWindowHeight();
FontColor.a = 255;
FontColor.r = 255;
FontColor.g = 0;
FontColor.b = 0;
}
PageBuilder::~PageBuilder()
{
}
Page *PageBuilder::BuildPage()
{
Page *page = NULL;
std::string layoutFile;
std::string layoutName = LayoutKey;
LayoutPath = Configuration::GetAbsolutePath() + "/Layouts/" + layoutName;
layoutFile = LayoutPath + "/Layout.xml";
Logger::Write(Logger::ZONE_INFO, "Layout", "Initializing " + layoutFile);
rapidxml::xml_document<> doc;
std::ifstream file(layoutFile.c_str());
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
if(!file.good())
{
Logger::Write(Logger::ZONE_INFO, "Layout", "could not find layout file: " + layoutFile);
return NULL;
}
try
{
buffer.push_back('\0');
doc.parse<0>(&buffer[0]);
xml_node<> *root = doc.first_node("layout");
if(!root)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Missing <layout> tag");
return NULL;
}
else
{
xml_attribute<> *layoutWidthXml = root->first_attribute("width");
xml_attribute<> *layoutHeightXml = root->first_attribute("height");
xml_attribute<> *fontXml = root->first_attribute("font");
xml_attribute<> *fontColorXml = root->first_attribute("fontColor");
int layoutHeight;
int layoutWidth;
if(!layoutWidthXml || !layoutHeightXml)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "<layout> tag must specify a width and height");
return NULL;
}
if(fontXml)
{
//todo: reuse from ComponentBuilder. Not sure how since it relies on knowing the collection
std::string fontPropertyKey = "layouts." + LayoutKey + ".font";
Config.SetProperty(fontPropertyKey, fontXml->value());
Font = Config.ConvertToAbsolutePath(
Config.GetAbsolutePath() + "/Layouts/" + LayoutKey + "/",
fontXml->value());
Logger::Write(Logger::ZONE_DEBUG, "Layout", "Layout font set to " + Font);
}
if(fontColorXml)
{
int intColor = 0;
std::stringstream ss;
ss << std::hex << fontColorXml->value();
ss >> intColor;
FontColor.b = intColor & 0xFF;
intColor >>= 8;
FontColor.g = intColor & 0xFF;
intColor >>= 8;
FontColor.r = intColor & 0xFF;
}
layoutWidth = Utils::ConvertInt(layoutWidthXml->value());
layoutHeight = Utils::ConvertInt(layoutHeightXml->value());
if(layoutWidth == 0 || layoutHeight == 0)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Layout width and height cannot be set to 0");
return NULL;
}
ScaleX = (float)ScreenWidth / (float)layoutWidth;
ScaleY = (float)ScreenHeight / (float)layoutHeight;
std::stringstream ss;
ss << layoutWidth << "x" << layoutHeight << " (scale " << ScaleX << "x" << ScaleY << ")";
Logger::Write(Logger::ZONE_DEBUG, "Layout", "Layout resolution " + ss.str());
page = new Page(Collection);
// load sounds
for(xml_node<> *sound = root->first_node("sound"); sound; sound = sound->next_sibling("sound"))
{
xml_attribute<> *src = sound->first_attribute("src");
xml_attribute<> *type = sound->first_attribute("type");
std::string file = Configuration::ConvertToAbsolutePath(LayoutPath, src->value());
if(!type)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Sound tag missing type attribute");
}
else
{
Sound *sound = new Sound(file);
std::string soundType = type->value();
if(!soundType.compare("load"))
{
page->SetLoadSound(sound);
}
else if(!soundType.compare("unload"))
{
page->SetUnloadSound(sound);
}
else if(!soundType.compare("highlight"))
{
page->SetHighlightSound(sound);
}
else if(!soundType.compare("select"))
{
page->SetSelectSound(sound);
}
else
{
Logger::Write(Logger::ZONE_WARNING, "Layout", "Unsupported sound effect type \"" + soundType + "\"");
}
}
}
if(!BuildComponents(root, page))
{
delete page;
page = NULL;
}
}
}
catch(rapidxml::parse_error &e)
{
std::string what = e.what();
long line = static_cast<long>(std::count(&buffer.front(), e.where<char>(), char('\n')) + 1);
std::stringstream ss;
ss << "Could not parse layout file. [Line: " << line << "] Reason: " << e.what();
Logger::Write(Logger::ZONE_ERROR, "Layout", ss.str());
}
catch(std::exception &e)
{
std::string what = e.what();
Logger::Write(Logger::ZONE_ERROR, "Layout", "Could not parse layout file. Reason: " + what);
}
if(page)
{
Logger::Write(Logger::ZONE_DEBUG, "Layout", "Created page");
}
return page;
}
float PageBuilder::GetHorizontalAlignment(xml_attribute<> *attribute, float valueIfNull)
{
float value;
std::string str;
if(!attribute)
{
value = valueIfNull;
}
else
{
str = attribute->value();
if(!str.compare("left"))
{
value = 0;
}
else if(!str.compare("center"))
{
value = static_cast<float>(ScreenWidth) / 2;
}
else if(!str.compare("right") || !str.compare("stretch"))
{
value = static_cast<float>(ScreenWidth);
}
else
{
value = Utils::ConvertFloat(str) * ScaleX;
}
}
return value;
}
float PageBuilder::GetVerticalAlignment(xml_attribute<> *attribute, float valueIfNull)
{
float value;
std::string str;
if(!attribute)
{
value = valueIfNull;
}
else
{
str = attribute->value();
if(!str.compare("top"))
{
value = 0;
}
else if(!str.compare("center"))
{
value = static_cast<float>(ScreenHeight / 2);
}
else if(!str.compare("bottom") || !str.compare("stretch"))
{
value = static_cast<float>(ScreenHeight);
}
else
{
value = Utils::ConvertFloat(str) * ScaleY;
}
}
return value;
}
bool PageBuilder::BuildComponents(xml_node<> *layout, Page *page)
{
xml_node<> *menuXml = layout->first_node("menu");
if(!menuXml)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Missing menu tag");
return false;
}
ScrollingList *scrollingList = BuildMenu(menuXml);
page->SetMenu(scrollingList);
for(xml_node<> *componentXml = layout->first_node("container"); componentXml; componentXml = componentXml->next_sibling("container"))
{
Container *c = new Container();
ViewInfo *v = c->GetBaseViewInfo();
BuildViewInfo(componentXml, v);
LoadTweens(c, componentXml);
page->AddComponent(c);
}
for(xml_node<> *componentXml = layout->first_node("image"); componentXml; componentXml = componentXml->next_sibling("image"))
{
xml_attribute<> *src = componentXml->first_attribute("src");
if (!src)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Image component in layout does not specify a source image file");
}
else
{
std::string imagePath;
imagePath = Configuration::ConvertToAbsolutePath(LayoutPath, imagePath);
imagePath.append("/");
imagePath.append(src->value());
Image *c = new Image(imagePath, ScaleX, ScaleY);
ViewInfo *v = c->GetBaseViewInfo();
BuildViewInfo(componentXml, v);
LoadTweens(c, componentXml);
page->AddComponent(c);
}
}
for(xml_node<> *componentXml = layout->first_node("text"); componentXml; componentXml = componentXml->next_sibling("text"))
{
xml_attribute<> *value = componentXml->first_attribute("value");
if (!value)
{
Logger::Write(Logger::ZONE_WARNING, "Layout", "Text component in layout does not specify a value");
}
else
{
FC->LoadFont(Font, FontColor);
Text *c = new Text(value->value(), FC->GetFont(Font), FontColor, ScaleX, ScaleY);
ViewInfo *v = c->GetBaseViewInfo();
BuildViewInfo(componentXml, v);
LoadTweens(c, componentXml);
page->AddComponent(c);
}
}
LoadReloadableImages(layout, "reloadableImage", page);
LoadReloadableImages(layout, "reloadableVideo", page);
LoadReloadableImages(layout, "reloadableText", page);
return true;
}
void PageBuilder::LoadReloadableImages(xml_node<> *layout, std::string tagName, Page *page)
{
for(xml_node<> *componentXml = layout->first_node(tagName.c_str()); componentXml; componentXml = componentXml->next_sibling(tagName.c_str()))
{
std::string reloadableImagePath;
std::string reloadableVideoPath;
xml_attribute<> *type = componentXml->first_attribute("type");
if(tagName == "reloadableVideo")
{
type = componentXml->first_attribute("imageType");
}
if(!type && tagName == "reloadableVideo")
{
Logger::Write(Logger::ZONE_WARNING, "Layout", "<reloadableImage> component in layout does not specify an imageType for when the video does not exist");
}
if(!type && (tagName == "reloadableImage" || tagName == "reloadableText"))
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Image component in layout does not specify a source image file");
}
if(type && (tagName == "reloadableVideo" || tagName == "reloadableImage"))
{
std::string configImagePath = "collections." + Collection + ".media." + type->value();
if(!Config.GetPropertyAbsolutePath(configImagePath, reloadableImagePath))
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Cannot process reloadable images because property \"" + configImagePath + "\" does not exist");
}
std::string configVideoPath = "collections." + Collection + ".media.video";
if(!Config.GetPropertyAbsolutePath(configVideoPath, reloadableVideoPath))
{
Logger::Write(Logger::ZONE_WARNING, "Layout", "Could not find videos folder as \"" + configVideoPath + "\" does not exist");
}
}
Component *c = NULL;
if(tagName == "reloadableText")
{
if(type)
{
FC->LoadFont(Font, FontColor);
c = new ReloadableText(type->value(), FC->GetFont(Font), FontColor, LayoutKey, Collection, ScaleX, ScaleY);
}
}
else
{
c = new ReloadableMedia(reloadableImagePath, reloadableVideoPath, (tagName == "reloadableVideo"), ScaleX, ScaleY);
}
if(c)
{
LoadTweens(c, componentXml);
page->AddComponent(c);
}
}
}
void PageBuilder::LoadTweens(Component *c, xml_node<> *componentXml)
{
ViewInfo *v = c->GetBaseViewInfo();
BuildViewInfo(componentXml, v);
Component::TweenSets *tweenSets;
tweenSets = new std::vector<std::vector<Tween *> *>();
GetTweenSets(componentXml->first_node("onEnter"), tweenSets);
c->SetOnEnterTweens(tweenSets);
tweenSets = new std::vector<std::vector<Tween *> *>();
GetTweenSets(componentXml->first_node("onExit"), tweenSets);
c->SetOnExitTweens(tweenSets);
tweenSets = new std::vector<std::vector<Tween *> *>();
GetTweenSets(componentXml->first_node("onIdle"), tweenSets);
c->SetOnIdleTweens(tweenSets);
tweenSets = new std::vector<std::vector<Tween *> *>();
GetTweenSets(componentXml->first_node("onHighlightEnter"), tweenSets);
c->SetOnHighlightEnterTweens(tweenSets);
tweenSets = new std::vector<std::vector<Tween *> *>();
GetTweenSets(componentXml->first_node("onHighlightExit"), tweenSets);
c->SetOnHighlightExitTweens(tweenSets);
}
ScrollingList * PageBuilder::BuildMenu(xml_node<> *menuXml)
{
ScrollingList *menu = NULL;
std::string menuType = "vertical";
std::string imageType = "null";
std::map<int, xml_node<> *> overrideItems;
xml_node<> *itemDefaults = menuXml->first_node("itemDefaults");
xml_attribute<> *imageTypeXml = menuXml->first_attribute("imageType");
xml_attribute<> *menuTypeXml = menuXml->first_attribute("type");
if(menuTypeXml)
{
menuType = menuTypeXml->value();
}
// ensure <menu> has an <itemDefaults> tag
if(!itemDefaults)
{
Logger::Write(Logger::ZONE_WARNING, "Layout", "Menu tag is missing <itemDefaults> tag.");
}
if(imageTypeXml)
{
imageType = imageTypeXml->value();
}
// on default, text will be rendered to the menu. Preload it into cache.
FC->LoadFont(Font, FontColor);
menu = new ScrollingList(Config, ScaleX, ScaleY, FC->GetFont(Font), FontColor, LayoutKey, Collection, imageType);
ViewInfo *v = menu->GetBaseViewInfo();
BuildViewInfo(menuXml, v);
if(menuType == "custom")
{
BuildCustomMenu(menu, menuXml, itemDefaults);
}
else
{
BuildVerticalMenu(menu, menuXml, itemDefaults);
}
return menu;
}
void PageBuilder::BuildCustomMenu(ScrollingList *menu, xml_node<> *menuXml, xml_node<> *itemDefaults)
{
std::vector<ViewInfo *> *points = new std::vector<ViewInfo *>();
int i = 0;
for(xml_node<> *componentXml = menuXml->first_node("item"); componentXml; componentXml = componentXml->next_sibling("item"))
{
ViewInfo *viewInfo = new ViewInfo();
BuildViewInfo(componentXml, viewInfo, itemDefaults);
points->push_back(viewInfo);
xml_attribute<> *selected = componentXml->first_attribute("selected");
if(selected)
{
menu->SetSelectedIndex(i);
}
i++;
}
menu->SetPoints(points);
}
void PageBuilder::BuildVerticalMenu(ScrollingList *menu, xml_node<> *menuXml, xml_node<> *itemDefaults)
{
std::vector<ViewInfo *> *points = new std::vector<ViewInfo *>();
int selectedIndex = MENU_FIRST;
std::map<int, xml_node<> *> overrideItems;
// By default the menu will automatically determine the offsets for your list items.
// We can override individual menu points to have unique characteristics (i.e. make the first item opaque or
// make the selected item a different color).
for(xml_node<> *componentXml = menuXml->first_node("item"); componentXml; componentXml = componentXml->next_sibling("item"))
{
xml_attribute<> *xmlIndex = componentXml->first_attribute("index");
if(xmlIndex)
{
int itemIndex = ParseMenuPosition(xmlIndex->value());
overrideItems[itemIndex] = componentXml;
// check to see if the item specified is the selected index
xml_attribute<> *xmlSelectedIndex = componentXml->first_attribute("selected");
if(xmlSelectedIndex)
{
selectedIndex = itemIndex;
}
}
}
bool end = false;
//menu start
float height = 0;
int index = 0;
if(overrideItems.find(MENU_START) != overrideItems.end())
{
xml_node<> *component = overrideItems[MENU_START];
ViewInfo *viewInfo = CreateMenuItemInfo(component, itemDefaults, menu->GetBaseViewInfo()->GetY() + height);
points->push_back(viewInfo);
}
while(!end)
{
ViewInfo *viewInfo = new ViewInfo();
xml_node<> *component = itemDefaults;
// uss overridden item setting if specified by layout for the given index
if(overrideItems.find(index) != overrideItems.end())
{
component = overrideItems[index];
}
// calculate the total height of our menu items if we can load any additional items
BuildViewInfo(component, viewInfo, itemDefaults);
xml_attribute<> *itemSpacingXml = component->first_attribute("spacing");
int itemSpacing = itemSpacingXml ? Utils::ConvertInt(itemSpacingXml->value()) : 0;
float nextHeight = height + viewInfo->GetHeight() + itemSpacing;
if(nextHeight >= menu->GetBaseViewInfo()->GetHeight())
{
end = true;
}
// we have reached the last menuitem
if(end && overrideItems.find(MENU_LAST) != overrideItems.end())
{
component = overrideItems[MENU_LAST];
BuildViewInfo(component, viewInfo, itemDefaults);
xml_attribute<> *itemSpacingXml = component->first_attribute("spacing");
int itemSpacing = itemSpacingXml ? Utils::ConvertInt(itemSpacingXml->value()) : 0;
nextHeight = height + viewInfo->GetHeight() + itemSpacing;
}
height = nextHeight;
viewInfo->SetY(menu->GetBaseViewInfo()->GetY() + (float)height);
points->push_back(viewInfo);
index++;
}
//menu end
if(overrideItems.find(MENU_END) != overrideItems.end())
{
xml_node<> *component = overrideItems[MENU_END];
ViewInfo *viewInfo = CreateMenuItemInfo(component, itemDefaults, menu->GetBaseViewInfo()->GetY() + height);
points->push_back(viewInfo);
}
if(selectedIndex >= ((int)points->size()-2))
{
//todo: print debug statements when out of range
selectedIndex = 1;
}
else
{
menu->SetSelectedIndex(selectedIndex+1);
}
menu->SetPoints(points);
}
ViewInfo *PageBuilder::CreateMenuItemInfo(xml_node<> *component, xml_node<> *defaults, float y)
{
ViewInfo *viewInfo = new ViewInfo();
BuildViewInfo(component, viewInfo, defaults);
viewInfo->SetY(y);
return viewInfo;
}
int PageBuilder::ParseMenuPosition(std::string strIndex)
{
int index = MENU_FIRST;
if(strIndex == "end")
{
index = MENU_END;
}
else if(strIndex == "last")
{
index = MENU_LAST;
}
else if(strIndex == "start")
{
index = MENU_START;
}
else if(strIndex == "first")
{
index = MENU_FIRST;
}
else
{
index = Utils::ConvertInt(strIndex);
}
return index;
}
xml_attribute<> *PageBuilder::FindAttribute(xml_node<> *componentXml, std::string attribute, xml_node<> *defaultXml = NULL)
{
xml_attribute<> *attributeXml = componentXml->first_attribute(attribute.c_str());
if(!attributeXml && defaultXml)
{
attributeXml = defaultXml->first_attribute(attribute.c_str());
}
return attributeXml;
}
void PageBuilder::BuildViewInfo(xml_node<> *componentXml, ViewInfo *info, xml_node<> *defaultXml)
{
xml_attribute<> *x = FindAttribute(componentXml, "x", defaultXml);
xml_attribute<> *y = FindAttribute(componentXml, "y", defaultXml);
xml_attribute<> *xOffset = FindAttribute(componentXml, "xOffset", defaultXml);
xml_attribute<> *yOffset = FindAttribute(componentXml, "yOffset", defaultXml);
xml_attribute<> *xOrigin = FindAttribute(componentXml, "xOrigin", defaultXml);
xml_attribute<> *yOrigin = FindAttribute(componentXml, "yOrigin", defaultXml);
xml_attribute<> *height = FindAttribute(componentXml, "height", defaultXml);
xml_attribute<> *width = FindAttribute(componentXml, "width", defaultXml);
xml_attribute<> *fontSize = FindAttribute(componentXml, "fontSize", defaultXml);
xml_attribute<> *minHeight = FindAttribute(componentXml, "minHeight", defaultXml);
xml_attribute<> *minWidth = FindAttribute(componentXml, "minWidth", defaultXml);
xml_attribute<> *maxHeight = FindAttribute(componentXml, "maxHeight", defaultXml);
xml_attribute<> *maxWidth = FindAttribute(componentXml, "maxWidth", defaultXml);
xml_attribute<> *alpha = FindAttribute(componentXml, "alpha", defaultXml);
xml_attribute<> *angle = FindAttribute(componentXml, "angle", defaultXml);
xml_attribute<> *layer = FindAttribute(componentXml, "layer", defaultXml);
xml_attribute<> *backgroundColor = FindAttribute(componentXml, "backgroundColor", defaultXml);
xml_attribute<> *backgroundAlpha = FindAttribute(componentXml, "backgroundAlpha", defaultXml);
info->SetX(GetHorizontalAlignment(x, 0));
info->SetY(GetVerticalAlignment(y, 0));
info->SetXOffset( GetHorizontalAlignment(xOffset, 0));
info->SetYOffset( GetVerticalAlignment(yOffset, 0));
float xOriginRelative = GetHorizontalAlignment(xOrigin, 0);
float yOriginRelative = GetVerticalAlignment(yOrigin, 0);
// the origins need to be saved as a percent since the heights and widths can be scaled
info->SetXOrigin(xOriginRelative / ScreenWidth);
info->SetYOrigin(yOriginRelative / ScreenHeight);
if(!height && !width)
{
info->SetHeight(-1);
info->SetWidth(-1);
}
else
{
info->SetHeight(GetVerticalAlignment(height, -1));
info->SetWidth(GetHorizontalAlignment(width, -1));
}
info->SetFontSize(GetVerticalAlignment(fontSize, -1));
info->SetMinHeight(GetVerticalAlignment(minHeight, 0));
info->SetMinWidth(GetHorizontalAlignment(minWidth, 0));
info->SetMaxHeight(GetVerticalAlignment(maxHeight, FLT_MAX));
info->SetMaxWidth(GetVerticalAlignment(maxWidth, FLT_MAX));
info->SetAlpha( alpha ? Utils::ConvertFloat(alpha->value()) : 1);
info->SetAngle( angle ? Utils::ConvertFloat(angle->value()) : 0);
info->SetLayer( layer ? Utils::ConvertInt(layer->value()) : 0);
if(backgroundColor)
{
std::stringstream ss(backgroundColor->value());
int num;
ss >> std::hex >> num;
int red = num / 0x10000;
int green = (num / 0x100) % 0x100;
int blue = num % 0x100;
info->SetBackgroundRed(static_cast<float>(red)/255);
info->SetBackgroundGreen(static_cast<float>(green)/255);
info->SetBackgroundBlue(static_cast<float>(blue)/255);
}
if(backgroundAlpha)
{
std::stringstream ss(backgroundAlpha->value());
int num;
ss >> std::hex >> num;
info->SetBackgroundAlpha(static_cast<float>(num)/255);
}
}
void PageBuilder::GetTweenSets(xml_node<> *node, std::vector<std::vector<Tween *> *> *tweenSets)
{
if(node)
{
for(xml_node<> *set = node->first_node("set"); set; set = set->next_sibling("set"))
{
std::vector<Tween *> *tweens = new std::vector<Tween *>();
GetTweenSet(set, *tweens);
tweenSets->push_back(tweens);
}
}
}
void PageBuilder::GetTweenSet(xml_node<> *node, std::vector<Tween *> &tweens)
{
xml_attribute<> *durationXml = node->first_attribute("duration");
if(!durationXml)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Animation set tag missing \"duration\" attribute");
}
else
{
for(xml_node<> *animate = node->first_node("animate"); animate; animate = animate->next_sibling("animate"))
{
xml_attribute<> *type = animate->first_attribute("type");
xml_attribute<> *from = animate->first_attribute("from");
xml_attribute<> *to = animate->first_attribute("to");
xml_attribute<> *algorithmXml = animate->first_attribute("algorithm");
if(!type)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Animate tag missing \"type\" attribute");
}
else if(!from)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Animate tag missing \"from\" attribute");
}
else if(!to)
{
Logger::Write(Logger::ZONE_ERROR, "Layout", "Animate tag missing \"to\" attribute");
}
else
{
float fromValue = Utils::ConvertFloat(from->value());
float toValue = Utils::ConvertFloat(to->value());
float durationValue = Utils::ConvertFloat(durationXml->value());
TweenAlgorithm algorithm = LINEAR;
TweenProperty property;
if(algorithmXml)
{
algorithm = Tween::GetTweenType(algorithmXml->value());
}
if(Tween::GetTweenProperty(type->value(), property))
{
switch(property)
{
case TWEEN_PROPERTY_WIDTH:
case TWEEN_PROPERTY_X:
case TWEEN_PROPERTY_X_OFFSET:
fromValue = GetHorizontalAlignment(from, 0);
toValue = GetHorizontalAlignment(to, 0);
break;
// x origin gets translated to a percent
case TWEEN_PROPERTY_X_ORIGIN:
fromValue = GetHorizontalAlignment(from, 0) / ScreenWidth;
toValue = GetHorizontalAlignment(to, 0) / ScreenWidth;
break;
case TWEEN_PROPERTY_HEIGHT:
case TWEEN_PROPERTY_Y:
case TWEEN_PROPERTY_Y_OFFSET:
case TWEEN_PROPERTY_FONT_SIZE:
fromValue = GetVerticalAlignment(from, 0);
toValue = GetVerticalAlignment(to, 0);
break;
// y origin gets translated to a percent
case TWEEN_PROPERTY_Y_ORIGIN:
fromValue = GetVerticalAlignment(from, 0) / ScreenHeight;
toValue = GetVerticalAlignment(to, 0) / ScreenHeight;
break;
default:
break;
}
Tween *t = new Tween(property, algorithm, fromValue, toValue, durationValue);
tweens.push_back(t);
}
else
{
std::stringstream ss;
ss << "Unsupported tween type attribute \"" << type->value() << "\"";
Logger::Write(Logger::ZONE_ERROR, "Layout", ss.str());
}
}
}
}
}

View File

@@ -0,0 +1,71 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Component/Image.h"
#include "FontCache.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <rapidxml.hpp>
#include <vector>
class ScrollingList;
class Page;
class ViewInfo;
class Configuration;
class PageBuilder
{
public:
PageBuilder(std::string layoutKey, std::string collection, Configuration &c, FontCache *fc);
virtual ~PageBuilder();
Page *BuildPage();
private:
std::string LayoutKey;
std::string LayoutPath;
std::string Collection;
Configuration &Config;
float ScaleX;
float ScaleY;
int ScreenHeight;
int ScreenWidth;
SDL_Color FontColor;
std::string Font;
FontCache *FC; //todo: don't need Font itself, just need cache instances
void LoadReloadableImages(rapidxml::xml_node<> *layout, std::string tagName, Page *page);
float GetVerticalAlignment(rapidxml::xml_attribute<> *attribute, float valueIfNull);
float GetHorizontalAlignment(rapidxml::xml_attribute<> *attribute, float valueIfNull);
void BuildViewInfo(rapidxml::xml_node<> *componentXml, ViewInfo *info, rapidxml::xml_node<> *defaultXml = NULL);
bool BuildComponents(rapidxml::xml_node<> *layout, Page *page);
void LoadTweens(Component *c, rapidxml::xml_node<> *componentXml);
ScrollingList * BuildMenu(rapidxml::xml_node<> *menuXml);
void BuildCustomMenu(ScrollingList *menu, rapidxml::xml_node<> *menuXml, rapidxml::xml_node<> *itemDefaults);
void BuildVerticalMenu(ScrollingList *menu, rapidxml::xml_node<> *menuXml, rapidxml::xml_node<> *itemDefaults);
int ParseMenuPosition(std::string strIndex);
rapidxml::xml_attribute<> *FindAttribute(rapidxml::xml_node<> *componentXml, std::string attribute, rapidxml::xml_node<> *defaultXml);
void GetTweenSets(rapidxml::xml_node<> *node, std::vector<std::vector<Tween *> *> *tweenSets);
void GetTweenSet(rapidxml::xml_node<> *node, std::vector<Tween *> &tweens);
void LoadLayoutXml();
void LoadAnimations(std::string keyPrefix, Component &component, ViewInfo *defaults);
std::vector<ViewInfo *> * BuildTweenPoints(std::string iteratorPrefix, ViewInfo *defaults);
Component * LoadComponent(std::string keyPrefix);
ScrollingList * LoadMenu();
ViewInfo * CreateMenuItemInfo(rapidxml::xml_node<> *component, rapidxml::xml_node<> *defaults, float y);
void LoadListItems(std::string keyPrefix, std::vector<ViewInfo *> *tweenPointList, ViewInfo *defaults, int &selectedItemIndex);
void UpdateViewInfoFromTag(std::string keyPrefix, ViewInfo *p, ViewInfo *defaults);
static const int MENU_FIRST = 0; // first visible item in the list
static const int MENU_LAST = -3; // last visible item in the list
static const int MENU_START = -1; // first item transitions here after it scrolls "off the menu/screen"
static const int MENU_END = -2; // last item transitions here after it scrolls "off the menu/screen"
static const int MENU_CENTER = -4;
};

View File

@@ -0,0 +1,357 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ViewInfo.h"
#include "../Database/Configuration.h"
#include "Animate/TweenTypes.h"
#include <cfloat>
ViewInfo::ViewInfo()
: X(0)
, Y(0)
, XOrigin(0)
, YOrigin(0)
, XOffset(0)
, YOffset(0)
, Width(-1)
, MinWidth(0)
, MaxWidth(FLT_MAX)
, Height(-1)
, MinHeight(0)
, MaxHeight(FLT_MAX)
, ImageWidth(0)
, ImageHeight(0)
, FontSize(-1)
, Angle(0)
, Alpha(1)
, Layer(0)
, BackgroundRed(0)
, BackgroundGreen(0)
, BackgroundBlue(0)
, BackgroundAlpha(0)
{
}
ViewInfo::~ViewInfo()
{
}
float ViewInfo::GetXRelativeToOrigin() const
{
return X + XOffset - XOrigin*GetWidth();
}
float ViewInfo::GetYRelativeToOrigin() const
{
return Y + YOffset - YOrigin*GetHeight();
}
float ViewInfo::GetHeight() const
{
float value = Height;
if(Height == -1 && Width == -1)
{
value = ImageHeight;
}
else
{
if (Height == -1 && ImageWidth != 0)
{
value = ImageHeight * Width / ImageWidth;
}
if (value < MinHeight)
{
value = MinHeight;
}
else if (value > MaxHeight)
{
value = MaxHeight;
}
}
return value;
}
float ViewInfo::GetWidth() const
{
float value = Width;
if(Height == -1 && Width == -1)
{
value = ImageWidth;
}
else
{
if (Width == -1 && ImageHeight != 0)
{
value = ImageWidth * Height / ImageHeight;
}
if (value < MinWidth)
{
value = MinWidth;
}
else if (value > MaxWidth)
{
value = MaxWidth;
}
}
return value;
}
float ViewInfo::GetXOffset() const
{
return XOffset;
}
float ViewInfo::GetXOrigin() const
{
return XOrigin;
}
float ViewInfo::GetYOffset() const
{
return YOffset;
}
float ViewInfo::GetYOrigin() const
{
return YOrigin;
}
float ViewInfo::GetAngle() const
{
return Angle;
}
void ViewInfo::SetAngle(float angle)
{
Angle = angle;
}
float ViewInfo::GetImageHeight() const
{
return ImageHeight;
}
void ViewInfo::SetImageHeight(float imageheight)
{
ImageHeight = imageheight;
}
float ViewInfo::GetImageWidth() const
{
return ImageWidth;
}
void ViewInfo::SetImageWidth(float imagewidth)
{
ImageWidth = imagewidth;
}
unsigned int ViewInfo::GetLayer() const
{
return Layer;
}
void ViewInfo::SetLayer(unsigned int layer)
{
Layer = layer;
}
float ViewInfo::GetMaxHeight() const
{
return MaxHeight;
}
void ViewInfo::SetMaxHeight(float maxheight)
{
MaxHeight = maxheight;
}
float ViewInfo::GetMaxWidth() const
{
return MaxWidth;
}
void ViewInfo::SetMaxWidth(float maxwidth)
{
MaxWidth = maxwidth;
}
float ViewInfo::GetMinHeight() const
{
return MinHeight;
}
void ViewInfo::SetMinHeight(float minheight)
{
MinHeight = minheight;
}
float ViewInfo::GetMinWidth() const
{
return MinWidth;
}
void ViewInfo::SetMinWidth(float minwidth)
{
MinWidth = minwidth;
}
float ViewInfo::GetAlpha() const
{
return Alpha;
}
void ViewInfo::SetAlpha(float alpha)
{
Alpha = alpha;
}
float ViewInfo::GetX() const
{
return X;
}
void ViewInfo::SetX(float x)
{
X = x;
}
void ViewInfo::SetXOffset(float offset)
{
XOffset = offset;
}
void ViewInfo::SetXOrigin(float origin)
{
XOrigin = origin;
}
float ViewInfo::GetY() const
{
return Y;
}
void ViewInfo::SetY(float y)
{
Y = y;
}
void ViewInfo::SetYOffset(float offset)
{
YOffset = offset;
}
void ViewInfo::SetYOrigin(float origin)
{
YOrigin = origin;
}
float ViewInfo::GetRawYOrigin()
{
return YOrigin;
}
float ViewInfo::GetRawXOrigin()
{
return XOrigin;
}
float ViewInfo::GetRawWidth()
{
return Width;
}
float ViewInfo::GetRawHeight()
{
return Height;
}
void ViewInfo::SetHeight(float height)
{
Height = height;
}
void ViewInfo::SetWidth(float width)
{
Width = width;
}
float ViewInfo::GetFontSize() const
{
if(FontSize == -1)
{
return GetHeight();
}
else
{
return FontSize;
}
}
void ViewInfo::SetFontSize(float fontSize)
{
FontSize = fontSize;
}
float ViewInfo::GetBackgroundRed()
{
return BackgroundRed;
}
void ViewInfo::SetBackgroundRed(float value)
{
BackgroundRed = value;
}
float ViewInfo::GetBackgroundGreen()
{
return BackgroundGreen;
}
void ViewInfo::SetBackgroundGreen(float value)
{
BackgroundGreen = value;
}
float ViewInfo::GetBackgroundBlue()
{
return BackgroundBlue;
}
void ViewInfo::SetBackgroundBlue(float value)
{
BackgroundBlue = value;
}
float ViewInfo::GetBackgroundAlpha()
{
return BackgroundAlpha;
}
void ViewInfo::SetBackgroundAlpha(float value)
{
BackgroundAlpha = value;
}

View File

@@ -0,0 +1,102 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Animate/TweenTypes.h"
#include <string>
#include <map>
class ViewInfo
{
public:
ViewInfo();
virtual ~ViewInfo();
float GetXRelativeToOrigin() const;
float GetYRelativeToOrigin() const;
float GetHeight() const;
float GetWidth() const;
float GetAngle() const;
void SetAngle(float angle);
float GetImageHeight() const;
void SetImageHeight(float imageheight);
float GetImageWidth() const;
void SetImageWidth(float imagewidth);
unsigned int GetLayer() const;
void SetLayer(unsigned int layer);
float GetMaxHeight() const;
void SetMaxHeight(float maxheight);
float GetMaxWidth() const;
void SetMaxWidth(float maxwidth);
float GetMinHeight() const;
void SetMinHeight(float minheight);
float GetMinWidth() const;
void SetMinWidth(float minwidth);
float GetAlpha() const;
void SetAlpha(float alpha);
float GetX() const;
void SetX(float x);
float GetXOffset() const;
void SetXOffset(float offset);
float GetXOrigin() const;
void SetXOrigin(float origin);
float GetY() const;
void SetY(float y);
float GetYOffset() const;
void SetYOffset(float offset);
float GetYOrigin() const;
void SetYOrigin(float origin);
float GetRawYOrigin();
float GetRawXOrigin();
float GetRawWidth();
float GetRawHeight();
float GetBackgroundRed();
void SetBackgroundRed(float value);
float GetBackgroundGreen();
void SetBackgroundGreen(float value);
float GetBackgroundBlue();
void SetBackgroundBlue(float value);
float GetBackgroundAlpha();
void SetBackgroundAlpha(float value);
void SetHeight(float height);
void SetWidth(float width);
float GetFontSize() const;
void SetFontSize(float fontSize);
static const int AlignCenter = -1;
static const int AlignLeft = -2;
static const int AlignTop = -3;
static const int AlignRight = -4;
static const int AlignBottom = -5;
private:
float X;
float Y;
float XOrigin;
float YOrigin;
float XOffset;
float YOffset;
float Width;
float MinWidth;
float MaxWidth;
float Height;
float MinHeight;
float MaxHeight;
float ImageWidth;
float ImageHeight;
float FontSize;
float Angle;
float Alpha;
unsigned int Layer;
float HorizontalScale;
float VerticalScale;
float BackgroundRed;
float BackgroundGreen;
float BackgroundBlue;
float BackgroundAlpha;
};

207
RetroFE/Source/Main.cpp Normal file
View File

@@ -0,0 +1,207 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Database/Configuration.h"
#include "Database/CollectionDatabase.h"
#include "Collection/CollectionInfoBuilder.h"
#include "Collection/CollectionInfo.h"
#include "Database/DB.h"
#include "Database/MamelistMetadata.h"
#include "Execute/Launcher.h"
#include "Utility/Log.h"
#include "Utility/Utils.h"
#include "RetroFE.h"
#include "Version.h"
#include <cstdlib>
#include <fstream>
#include <dirent.h>
static bool ImportConfiguration(Configuration *c);
static bool StartLogging();
CollectionDatabase *InitializeCollectionDatabase(DB &db, Configuration &config);
int main(int argc, char *argv[])
{
Configuration::Initialize();
Configuration config;
if(!StartLogging())
{
return -1;
}
if(!ImportConfiguration(&config))
{
return -1;
}
DB db(Configuration::GetAbsolutePath() + "/cache.db");
if(!db.Initialize())
{
return -1;
}
CollectionDatabase *cdb = InitializeCollectionDatabase(db, config);
if(!cdb)
{
return -1;
}
RetroFE p(*cdb, config);
if(p.Initialize())
{
p.Run();
}
p.DeInitialize();
Logger::DeInitialize();
return 0;
}
bool ImportConfiguration(Configuration *c)
{
std::string configPath = Configuration::GetAbsolutePath();
std::string launchersPath = Configuration::GetAbsolutePath() + "/Launchers";
std::string collectionsPath = Configuration::GetAbsolutePath() + "/Collections";
DIR *dp;
struct dirent *dirp;
if(!c->Import("", configPath + "/Settings.conf"))
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not import \"" + configPath + "/Settings.conf\"");
return false;
}
if(!c->Import("controls", configPath + "/Controls.conf"))
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not import \"" + configPath + "/Settings.conf\"");
return false;
}
dp = opendir(launchersPath.c_str());
if(dp == NULL)
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not read directory \"" + launchersPath + "\"");
return false;
}
while((dirp = readdir(dp)) != NULL)
{
if (dirp->d_type != DT_DIR && std::string(dirp->d_name) != "." && std::string(dirp->d_name) != "..")
{
std::string basename = dirp->d_name;
// if(basename.length() > 0)
{
std::string extension = basename.substr(basename.find_last_of("."), basename.size()-1);
basename = basename.substr(0, basename.find_last_of("."));
if(extension == ".conf")
{
std::string prefix = "launchers." + basename;
std::string importFile = launchersPath + "/" + std::string(dirp->d_name);
if(!c->Import(prefix, importFile))
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not import \"" + importFile + "\"");
return false;
}
}
}
}
}
dp = opendir(collectionsPath.c_str());
if(dp == NULL)
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not read directory \"" + collectionsPath + "\"");
return false;
}
while((dirp = readdir(dp)) != NULL)
{
if (dirp->d_type == DT_DIR && std::string(dirp->d_name) != "." && std::string(dirp->d_name) != "..")
{
std::string prefix = "collections." + std::string(dirp->d_name);
std::string settingsFile = collectionsPath + "/" + dirp->d_name + "/Settings.conf";
if(!c->Import(prefix, settingsFile))
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not import \"" + settingsFile + "\"");
return false;
}
}
}
Logger::Write(Logger::ZONE_INFO, "RetroFE", "Imported configuration");
return true;
}
bool StartLogging()
{
std::string logFile = Configuration::GetAbsolutePath() + "/Log.txt";
if(!Logger::Initialize(logFile))
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not open \"" + logFile + "\" for writing");
return false;
}
Logger::Write(Logger::ZONE_INFO, "RetroFE", "Version " + Version::GetString() + " starting");
#ifdef WIN32
Logger::Write(Logger::ZONE_INFO, "RetroFE", "OS: Windows");
#else
Logger::Write(Logger::ZONE_INFO, "RetroFE", "OS: Linux");
#endif
Logger::Write(Logger::ZONE_INFO, "RetroFE", "Absolute path: " + Configuration::GetAbsolutePath());
return true;
}
CollectionDatabase *InitializeCollectionDatabase(DB &db, Configuration &config)
{
CollectionDatabase *cdb = NULL;
std::string dbFile = (Configuration::GetAbsolutePath() + "/cache.db");
std::ifstream infile(dbFile.c_str());
cdb = new CollectionDatabase(db, config);
if(!cdb->Initialize())
{
delete cdb;
cdb = NULL;
}
else if(!cdb->Import())
{
delete cdb;
cdb = NULL;
}
return cdb;
}

445
RetroFE/Source/RetroFE.cpp Normal file
View File

@@ -0,0 +1,445 @@
/* This file is part of RetroFE.
*
* RetroFE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RetroFE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RetroFE. If not, see <http://www.gnu.org/licenses/>.
*/
#include "RetroFE.h"
#include "Database/CollectionDatabase.h"
#include "Database/Configuration.h"
#include "Collection/Item.h"
#include "Execute/Launcher.h"
#include "Utility/Log.h"
#include "Collection/MenuParser.h"
#include "SDL.h"
#include "Control/UserInput.h"
#include "Graphics/PageBuilder.h"
#include "Graphics/Page.h"
#include "Graphics/Component/ScrollingList.h"
#include "Video/VideoFactory.h"
#include <vector>
#include <string>
#include <sstream>
#ifdef WIN32
#include <Windows.h>
#include <SDL2/SDL_syswm.h>
#endif
RetroFE::RetroFE(CollectionDatabase &db, Configuration &c)
: Config(c)
, CollectionDB(db)
, Input(Config)
, KeyInputDisable(0)
, CurrentTime(0)
, VideoInst(NULL)
{
}
RetroFE::~RetroFE()
{
DeInitialize();
}
void RetroFE::Render()
{
SDL_LockMutex(SDL::GetMutex());
SDL_SetRenderDrawColor(SDL::GetRenderer(), 0x0, 0x0, 0x00, 0xFF);
SDL_RenderClear(SDL::GetRenderer());
Page *page = PageChain.back();
if(page)
{
page->Draw();
}
SDL_RenderPresent(SDL::GetRenderer());
SDL_UnlockMutex(SDL::GetMutex());
}
bool RetroFE::Initialize()
{
Logger::Write(Logger::ZONE_INFO, "RetroFE", "Initializing");
if(!Input.Initialize()) return false;
if(!SDL::Initialize(Config)) return false;
FC.Initialize();
bool videoEnable = true;
int videoLoop = 0;
Config.GetProperty("videoEnable", videoEnable);
Config.GetProperty("videoLoop", videoLoop);
VideoFactory::SetEnabled(videoEnable);
VideoFactory::SetNumLoops(videoLoop);
VideoFactory vf;
VideoInst = vf.CreateVideo();
return true;
}
void RetroFE::LaunchEnter()
{
if(PageChain.size() > 0)
{
Page *p = PageChain.back();
p->LaunchEnter();
}
SDL_SetWindowGrab(SDL::GetWindow(), SDL_FALSE);
}
void RetroFE::LaunchExit()
{
SDL_RestoreWindow(SDL::GetWindow());
SDL_SetWindowGrab(SDL::GetWindow(), SDL_TRUE);
if(PageChain.size() > 0)
{
Page *p = PageChain.back();
p->LaunchExit();
}
}
void RetroFE::FreeGraphicsMemory()
{
if(PageChain.size() > 0)
{
Page *p = PageChain.back();
p->FreeGraphicsMemory();
}
FC.DeInitialize();
SDL::DeInitialize();
}
void RetroFE::AllocateGraphicsMemory()
{
SDL::Initialize(Config);
FC.Initialize();
if(PageChain.size() > 0)
{
Page *p = PageChain.back();
p->AllocateGraphicsMemory();
p->Start();
}
}
bool RetroFE::DeInitialize()
{
bool retVal = true;
FreeGraphicsMemory();
bool videoEnable = true;
while(PageChain.size() > 0)
{
Page *page = PageChain.back();
delete page;
PageChain.pop_back();
}
if(VideoInst)
{
delete VideoInst;
VideoInst = NULL;
}
//todo: handle video deallocation
return retVal;
}
void RetroFE::Run()
{
int attractModeTime = 0;
std::string firstCollection = "Main";
bool running = true;
Item *nextPageItem = NULL;
bool adminMode = false;
RETROFE_STATE state = RETROFE_IDLE;
Config.GetProperty("attractModeTime", attractModeTime);
Config.GetProperty("firstCollection", firstCollection);
Attract.SetIdleTime(static_cast<float>(attractModeTime));
LoadPage(firstCollection);
while (running)
{
float lastTime = 0;
float deltaTime = 0;
Page *page = PageChain.back();
Launcher l(*this, Config);
if(!page)
{
Logger::Write(Logger::ZONE_WARNING, "RetroFE", "Could not load page");
running = false;
break;
}
// todo: This could be transformed to use the state design pattern.
switch(state)
{
case RETROFE_IDLE:
state = ProcessUserInput(page);
break;
case RETROFE_NEXT_PAGE_REQUEST:
page->Stop();
state = RETROFE_NEXT_PAGE_WAIT;
break;
case RETROFE_NEXT_PAGE_WAIT:
if(page->IsHidden())
{
page = LoadPage(NextPageItem->GetName());
state = RETROFE_NEW;
}
break;
case RETROFE_LAUNCH_REQUEST:
l.Run(page->GetCollectionName(), NextPageItem);
state = RETROFE_IDLE;
break;
case RETROFE_BACK_REQUEST:
page->Stop();
state = RETROFE_BACK_WAIT;
break;
case RETROFE_BACK_WAIT:
if(page->IsHidden())
{
PageChain.pop_back();
delete page;
page = PageChain.back();
CurrentTime = (float)SDL_GetTicks() / 1000;
page->AllocateGraphicsMemory();
page->Start();
state = RETROFE_NEW;
}
break;
case RETROFE_NEW:
if(page->IsIdle())
{
state = RETROFE_IDLE;
}
break;
case RETROFE_QUIT_REQUEST:
page->Stop();
state = RETROFE_QUIT;
break;
case RETROFE_QUIT:
if(page->IsHidden())
{
running = false;
}
break;
}
// the logic below could be done in a helper method
if(running)
{
lastTime = CurrentTime;
CurrentTime = (float)SDL_GetTicks() / 1000;
if (CurrentTime < lastTime)
{
CurrentTime = lastTime;
}
deltaTime = CurrentTime - lastTime;
double sleepTime = 1000.0/60.0 - deltaTime*1000;
if(sleepTime > 0)
{
SDL_Delay(static_cast<unsigned int>(sleepTime));
}
Attract.Update(deltaTime, *page);
page->Update(deltaTime);
Render();
}
}
}
bool RetroFE::Back(bool &exit)
{
bool canGoBack = false;
bool exitOnBack = false;
Config.GetProperty("exitOnFirstPageBack", exitOnBack);
exit = false;
if(PageChain.size() > 1)
{
Page *page = PageChain.back();
page->Stop();
canGoBack = true;
}
else if(PageChain.size() == 1 && exitOnBack)
{
Page *page = PageChain.back();
page->Stop();
exit = true;
canGoBack = true;
}
return canGoBack;
}
RetroFE::RETROFE_STATE RetroFE::ProcessUserInput(Page *page)
{
SDL_Event e;
bool exit = false;
RETROFE_STATE state = RETROFE_IDLE;
if (SDL_PollEvent(&e) == 0) return state;
if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
{
const Uint8 *keys = SDL_GetKeyboardState(NULL);
Attract.Reset();
if (keys[Input.GetScancode(UserInput::KeyCodePreviousItem)])
{
page->SetScrolling(Page::ScrollDirectionBack);
}
if (keys[Input.GetScancode(UserInput::KeyCodeNextItem)])
{
page->SetScrolling(Page::ScrollDirectionForward);
}
if (keys[Input.GetScancode(UserInput::KeyCodePageUp)])
{
page->PageScroll(Page::ScrollDirectionBack);
}
if (keys[Input.GetScancode(UserInput::KeyCodePageDown)])
{
page->PageScroll(Page::ScrollDirectionForward);
}
if (keys[Input.GetScancode(UserInput::KeyCodeAdminMode)])
{
//todo: add admin mode support
}
if (keys[Input.GetScancode(UserInput::KeyCodeSelect)])
{
NextPageItem = page->GetSelectedItem();
if(NextPageItem)
{
state = (NextPageItem->IsLeaf()) ? RETROFE_LAUNCH_REQUEST : RETROFE_NEXT_PAGE_REQUEST;
}
}
if (keys[Input.GetScancode(UserInput::KeyCodeBack)])
{
if(Back(exit))
{
state = (exit) ? RETROFE_QUIT_REQUEST : RETROFE_BACK_REQUEST;
}
}
if (keys[Input.GetScancode(UserInput::KeyCodeQuit)])
{
state = RETROFE_QUIT_REQUEST;
}
if(!keys[Input.GetScancode(UserInput::KeyCodePreviousItem)] &&
!keys[Input.GetScancode(UserInput::KeyCodeNextItem)] &&
!keys[Input.GetScancode(UserInput::KeyCodePageUp)] &&
!keys[Input.GetScancode(UserInput::KeyCodePageDown)])
{
page->SetScrolling(Page::ScrollDirectionIdle);
}
}
return state;
}
Page *RetroFE::LoadPage(std::string collectionName)
{
Logger::Write(Logger::ZONE_INFO, "RetroFE", "Creating page for collection " + collectionName);
Page *page = NULL;
std::vector<Item *> *collection = GetCollection(collectionName);
std::string layoutName = GetLayout(collectionName);
if(PageChain.size() > 0)
{
Page *oldPage = PageChain.back();
oldPage->FreeGraphicsMemory();
}
PageBuilder pb(layoutName, collectionName, Config, &FC);
page = pb.BuildPage();
if(!page)
{
Logger::Write(Logger::ZONE_ERROR, "RetroFE", "Could not create page for " + collectionName);
}
else
{
page->SetItems(collection);
page->Start();
PageChain.push_back(page);
}
return page;
}
std::vector<Item *> *RetroFE::GetCollection(std::string collectionName)
{
std::vector<Item *> *collection = new std::vector<Item *>(); // the page will deallocate this once its done
MenuParser mp;
mp.GetMenuItems(&CollectionDB, collectionName, *collection);
CollectionDB.GetCollection(collectionName, *collection);
if(collection->size() == 0)
{
Logger::Write(Logger::ZONE_WARNING, "RetroFE", "No list items found for collection " + collectionName);
}
return collection;
}
std::string RetroFE::GetLayout(std::string collectionName)
{
std::string layoutKeyName = "collections." + collectionName + ".layout";
std::string layoutName = "Default 16x9";
if(!Config.GetProperty(layoutKeyName, layoutName))
{
Config.GetProperty("layout", layoutName);
}
return layoutName;
}

64
RetroFE/Source/RetroFE.h Normal file
View File

@@ -0,0 +1,64 @@
/* This file is subject to the terms and conditions defined in
* file 'LICENSE.txt', which is part of this source code package.
*/
#pragma once
#include "Collection/Item.h"
#include "Control/UserInput.h"
#include "Execute/AttractMode.h"
#include "Graphics/FontCache.h"
#include "Video/IVideo.h"
#include <SDL2/SDL.h>
#include <list>
#include <vector>
class CollectionDatabase;
class Configuration;
class Page;
class RetroFE
{
public:
RetroFE(CollectionDatabase &db, Configuration &c);
virtual ~RetroFE();
bool Initialize();
bool DeInitialize();
void Run();
void FreeGraphicsMemory();
void AllocateGraphicsMemory();
void LaunchEnter();
void LaunchExit();
private:
enum RETROFE_STATE
{
RETROFE_IDLE,
RETROFE_NEXT_PAGE_REQUEST,
RETROFE_NEXT_PAGE_WAIT,
RETROFE_LAUNCH_REQUEST,
RETROFE_BACK_REQUEST,
RETROFE_BACK_WAIT,
RETROFE_NEW,
RETROFE_QUIT_REQUEST,
RETROFE_QUIT,
};
void Render();
bool Back(bool &exit);
void Quit();
Page *LoadPage(std::string collectionName);
RETROFE_STATE ProcessUserInput(Page *page);
void Update(float dt, bool scrollActive);
std::string GetLayout(std::string collectionName);
std::vector<Item *> *GetCollection(std::string collectionName);
Configuration &Config;
CollectionDatabase &CollectionDB;
UserInput Input;
std::list<Page *> PageChain;
float KeyInputDisable;
float CurrentTime;
Item *NextPageItem;
FontCache FC;
IVideo *VideoInst;
AttractMode Attract;
};

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