/** * cemu_UI * * Copyright 2017-2019 <@Seil0> * * This program is free software; you can 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.cemu_UI.controller; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.w3c.dom.Document; import org.xml.sax.SAXException; import com.cemu_UI.datatypes.UIROMDataType; import javafx.collections.FXCollections; import javafx.collections.ObservableList; public class DBController { private static DBController instance = null; private ArrayList entries = new ArrayList<>(); private String DB_PATH_LocalGames; private String DB_PATH_ReverenceGames; private Connection connectionLocal = null; private Connection connectionReverence = null; private static final Logger LOGGER = LogManager.getLogger(DBController.class.getName()); public DBController() { } public static DBController getInstance() { if (instance == null) { instance = new DBController(); } return instance; } /** * initialize the {@link DBController} with the database connection check if * there is a need to create a new database refresh the database */ public void init() { initDatabaseConnection(); createRomDatabase(); } /** * create a new connection to the HomeFlix.db database * AutoCommit is set to false to prevent some issues, so manual commit is active! * * TODO this should be called LocalGames */ private void initDatabaseConnection() { DB_PATH_LocalGames = XMLController.getDirCemuUI() + "/localRoms.db"; DB_PATH_ReverenceGames = XMLController.getRference_gamesFile().getAbsolutePath(); try { // create a database connection connectionLocal = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH_LocalGames); connectionLocal.setAutoCommit(false); connectionReverence = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH_ReverenceGames); connectionReverence.setAutoCommit(false); } catch (SQLException e) { // if the error message is "out of memory", it probably means no database file is found LOGGER.error("error while loading the Local- or ReverenceGames database", e); } LOGGER.info("Local- and ReverenceGames database loaded successfull"); } /** * creating the local_roms table in localRoms.db * if the table has no entries, call loadRomDirectory * * TODO the local_roms table should be called LocalGames */ void createRomDatabase() { try { Statement stmt = connectionLocal.createStatement(); stmt.executeUpdate("create table if not exists local_roms (title, coverPath, romPath, titleID, productCode, region, lastPlayed, timePlayed)"); connectionLocal.commit(); stmt.close(); } catch (SQLException e) { LOGGER.error("error while creating local-games database", e); } } /** * refresh database to contain all (new added) games */ public void refreshDataBase() { LOGGER.info("<========== starting refreshing database ==========>"); entries.clear(); try { Statement stmt = connectionLocal.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM local_roms"); while (rs.next()) { entries.add(rs.getString(2)); } stmt.close(); rs.close(); } catch (SQLException e) { LOGGER.error("error while loading ROMs from ROM database, local_roms table", e); } if (entries.size() == 0) { loadAllGames(); } LOGGER.info("<========== finished refreshing database ==========>"); } // add a game to the database public void addGame(String title, String coverPath, String romPath, String titleID, String productCode, String region, String lastPlayed, String timePlayed) throws SQLException{ Statement stmt = connectionLocal.createStatement(); stmt.executeUpdate("insert into local_roms values ('"+title+"','"+coverPath+"','"+romPath+"','"+titleID+"'," + "'"+productCode+"','"+region+"','"+lastPlayed+"','"+timePlayed+"')"); connectionLocal.commit(); stmt.close(); LOGGER.info("added \""+title+"\" to ROM database"); } // remove a game from the database public void removeGame(String titleID) throws SQLException{ Statement stmt = connectionLocal.createStatement(); stmt.executeUpdate("delete from local_roms where titleID = '"+titleID+"'"); connectionLocal.commit(); stmt.close(); LOGGER.info("removed \""+titleID+"\" from ROM database"); } /** * load all games from the database to a ObservableList, order entries by title * @return a ObservableList that contains all local games from the database */ public ObservableList loadAllGames() { ObservableList games = FXCollections.observableArrayList(); LOGGER.info("loading all local games from the database ..."); try { Statement stmt = connectionLocal.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM local_roms"); while (rs.next()) { games.add(new UIROMDataType(rs.getString("romPath"), rs.getString("titleID"), rs.getString("title"), rs.getString("coverPath"))); } stmt.close(); rs.close(); } catch (Exception e) { LOGGER.error("error while loading all local games from the database", e); } return games; } /** * load one game from the database * @param titleID the titleID of the game you wish to get * @return the game you asked for */ public UIROMDataType loadSingleGame(String titleID) { UIROMDataType game = null; LOGGER.info("loading a single game (ID: {}) from the database ...", titleID); try { PreparedStatement ps = connectionLocal.prepareStatement("SELECT * FROM local_roms where titleID = ?"); ps.setString(1, titleID); ResultSet rs = ps.executeQuery(); while (rs.next()) { game = new UIROMDataType(rs.getString("romPath"), rs.getString("titleID"), rs.getString("title"), rs.getString("coverPath")); } rs.close(); ps.close(); }catch (Exception e){ LOGGER.error("error while loading a single game into the mainWindowController", e); } return game; } /** * get all .rpx files from a given directory and add them to the games database if they don't exist there * @param directory where to search for the .rpx files */ public void loadRomDirectory(String directory){ File dir = new File(directory); File appFile; String[] extensions = new String[] { "rpx", "jsp" }; File pictureCache = XMLController.getPictureCache(); String coverPath; try { Statement stmt = connectionReverence.createStatement(); List files = (List) FileUtils.listFiles(dir, extensions, true); LOGGER.info("<============================== start loading ROM Directory ==============================>"); LOGGER.info("Getting all .rpx files in " + dir.getCanonicalPath()+" including those in subdirectories"); // for all files in dir get the app.xml for (File file : files) { appFile = new File(file.getParent() + "/app.xml"); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(appFile); String title_ID = document.getElementsByTagName("title_id").item(0).getTextContent(); // get titile_ID from app.xml title_ID = title_ID.substring(0, 8) + "-" + title_ID.substring(8, title_ID.length()); LOGGER.info("Name: " + file.getName() + "; Title ID: " + title_ID); ResultSet rs = stmt.executeQuery("SELECT * FROM games WHERE TitleID = '" + title_ID + "';"); // for all elements in the games table check if it's already present, else add it while (rs.next()) { if (checkAddEntry(rs.getString(2))) { LOGGER.info(rs.getString(2) + ": game already in database"); } else { LOGGER.info("adding cover to cache ..."); BufferedImage originalImage = ImageIO.read(new URL(rs.getString(6)));// change path to where file is located int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType(); BufferedImage resizeImagePNG = resizeImage(originalImage, type, 400, 600); ImageIO.write(resizeImagePNG, "png", new File(pictureCache + "/" + rs.getString(3) + ".png")); coverPath = pictureCache + "/" + rs.getString(3) + ".png"; LOGGER.info(rs.getString(2) + ": adding ROM"); addGame(rs.getString(2), coverPath, file.getCanonicalPath(), rs.getString(1), rs.getString(3), rs.getString(5), "", "0"); } } } LOGGER.info("<============================= finished loading ROM Directory ============================>"); } catch (IOException | SQLException | ParserConfigurationException | SAXException e) { LOGGER.error("error while loading ROMs from directory", e); } } /** * check if there is a game with the given name already in the database * @param title game title * @return true if the game exists, false if not * @throws SQLException */ private boolean checkAddEntry(String title) throws SQLException{ Statement stmt = connectionLocal.createStatement(); boolean check = false; ResultSet rs = stmt.executeQuery("SELECT * FROM local_roms WHERE title = '"+title+"';"); while (rs.next()) { check = true; } return check; } @SuppressWarnings("unused") private void checkRemoveEntry() { /** * TODO needs to be implemented! * don't show ROM on the UI, but keep all parameter in case it's showing up again ask if old data should be used */ //LOGGER.info("check if entry removed not done yet!"); } /** * getting info for a game with titleID * @param titleID Title-ID of the Game * @return title, coverPath, romPath, titleID (in this order) */ public String[] getGameInfo(String titleID){ String[] gameInfo = new String[4]; LOGGER.info("getting game info for titleID: "+titleID+" ..."); try { Statement stmt = connectionLocal.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM local_roms where titleID = '"+titleID+"'"); while (rs.next()) { gameInfo[0] = rs.getString(1);// title gameInfo[1] = rs.getString(2);// coverPath gameInfo[2] = rs.getString(3);// romPath gameInfo[3] = rs.getString(4);// titleID } stmt.close(); rs.close(); }catch (Exception e){ LOGGER.error("error while getting game info", e); } return gameInfo; } public void setGameInfo(String title, String coverPath, String romPath, String titleID){ LOGGER.info("setting game info for titleID: "+titleID+" ..."); try { Statement stmt = connectionLocal.createStatement(); stmt.executeUpdate("UPDATE local_roms SET title = '" + title + "', coverPath = '" + coverPath + "'," + " romPath = '" + romPath + "' WHERE titleID = '"+titleID+"';"); connectionLocal.commit(); stmt.close(); }catch (Exception e){ LOGGER.error("error while setting game info", e); } } public void setLastPlayed(String titleID){ try{ Statement stmt = connectionLocal.createStatement(); stmt.executeUpdate("UPDATE local_roms SET lastPlayed=date('now') WHERE titleID = '"+titleID+"';"); connectionLocal.commit(); stmt.close(); }catch(SQLException e){ LOGGER.error("failed to set the last played", e); } } public String getLastPlayed(String titleID){ String lastPlayed = null; try{ Statement stmt = connectionLocal.createStatement(); ResultSet rs = stmt.executeQuery("SELECT lastPlayed FROM local_roms WHERE titleID = '"+titleID+"';" ); lastPlayed = rs.getString(1); stmt.close(); rs.close(); }catch(SQLException e){ LOGGER.error("failed to get the last played", e); } return lastPlayed; } public void setTotalPlaytime(String timePlayed, String titleID){ try{ Statement stmt = connectionLocal.createStatement(); stmt.executeUpdate("UPDATE local_roms SET timePlayed='"+timePlayed+"' WHERE titleID = '"+titleID+"';"); connectionLocal.commit(); stmt.close(); }catch(SQLException e){ LOGGER.error("failed to set total play time", e); e.printStackTrace(); } } public String getTotalPlaytime(String titleID){ String timePlayed = null; try{ Statement stmt = connectionLocal.createStatement(); ResultSet rs = stmt.executeQuery("SELECT timePlayed FROM local_roms WHERE titleID = '"+titleID+"';" ); timePlayed = rs.getString(1); stmt.close(); rs.close(); }catch(SQLException e){ LOGGER.error("failed to get total play time", e); } return timePlayed; } private static BufferedImage resizeImage(BufferedImage originalImage, int type, int IMG_WIDTH, int IMG_HEIGHT) { BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type); Graphics2D g = resizedImage.createGraphics(); g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null); g.dispose(); return resizedImage; } }