/*
* #######################################################
*
* Copyright (c) 2013, A. Weinberger. All rights reserved.
* --------------------------------------------------------
*/
package ale.model;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.LinkedList;
import java.util.List;
import ale.Constants;
import ale.Constants.Error;
import ale.controller.Main;
import ale.controller.Settings;
import ale.controller.SystemInformation;
import ale.model.skin.Skin;
import ale.model.skin.SkinPreviewVO;
import ale.model.skin.ZipSkinLoader;
import ale.model.uiFile.UIBackground;
import ale.util.fileUtil.FileUtil;
/**
* ------------------------------------------------- <br/>
* Package: ale.model <br/>
* Class : IModel <br/>
* --------------------------- <br/>
* <br/>
* The <code>IModel</code> class represents the ... model of the program. It handles all around the skin management. <br/>
* The Model is the interface to methods which are used to implement the logic of the program.<br/> It contains the algorithm to
* change the skin and uifile.<br/> To use it, it has to be initialized. This method will return an error code if something went
* wrong. After that it will create some temporary files and changes some settings, which the shutdown method will undo(clean up).
* <br/>
* <br/>
* Last edited: 22.04.2013 <br/>
* ------------------------------------------------- <br/>
*/
public final class Model implements IModel {
private Path authuiSystemfilePath;
private Path basebrdSystemfilePath;
/**
* After an instance was created you should call initialize.
*/
public Model() {
}
/**
* Initializes the model and returns if an error occured. It controls if all files needed are available and cleans the workspace.
* The security of the systemfiles gets lowered to work with them.
*
* @return error
*/
@Override
public Error initialize() {
Error ret = null;
if ((ret == null) && !FileUtil.controlDirectory(Constants.PROGRAM_WORKBASE_PATH)) {
ret = Error.WORKBASE_MISSING;
}
if ((ret == null) && !controlBasefiles()) { // Counts the available files. Not that safe but better than nothing.
ret = Error.WORKBASE_CHANGED;
}
if ((ret == null) && !FileUtil.control(Constants.RESHACKER_PATH)) {
ret = Error.RESHACKER_MISSING;
}
if (ret == null) {
this.authuiSystemfilePath = SystemInformation.getSystemfile_Authui();
this.basebrdSystemfilePath = SystemInformation.getSystemfile_BaseBrd();
if (!FileUtil.control(this.authuiSystemfilePath) || !FileUtil.control(this.basebrdSystemfilePath)) {
ret = Error.WRONG_SYSFILE_PATH;
}
}
if (ret == null) {
ret = removeProtection();
}
if (ret == null) {
ret = cleanModel();
}
if (ret != null) {
LOGGER.debug("Initialisation return code: " + ret);
} else {
LOGGER.debug("Initialisation completed without error!");
}
return ret;
}
/**
* Deletes the temporary files and resets the systemfile protection.
*
*/
@Override
public void shutdown() {
FileUtil.setProtection(this.authuiSystemfilePath, true);
FileUtil.setProtection(this.basebrdSystemfilePath, true);
if (Files.exists(Constants.PROGRAM_TMP_PATH, LinkOption.NOFOLLOW_LINKS)) { // and deletes the temporary files.
try {
FileUtil.deleteDirectory(Constants.PROGRAM_TMP_PATH);
} catch (IOException e) {
; // Nothing i can do here. If they are still there then they will be deleted if the program restarts.
}
}
}
@Override
public Skin newSkin(String skinname, String author, String web, Path img) {
if ((skinname == null) || skinname.equals("")) {
IllegalArgumentException iae = new IllegalArgumentException("Name is null or empty!");
Main.handleUnhandableProblem(iae);
}
Error err = cleanModel(); // Delete all files which are from the old skin.
Skin skin = null;
if (err == null) {
skin = new Skin(Constants.RESHACKER_SCRIPT_PATH, Constants.SYSTEMFILE_AUTHUI_TMPPATH, Constants.UITEXT_TMP,
Constants.SYSTEMFILE_BASEBRD_TMPPATH);
try {
skin.create(skinname, author, web, img);
{
LOGGER.debug("Skin created!");
LOGGER.debug(skin.toString());
}
} catch (IOException e) {
LOGGER.error(e);
skin = null;
}
}
return skin;
}
@Override
public Skin loadSkin(Path skinPath) {
if (!FileUtil.control(skinPath)) {
IllegalArgumentException iae = new IllegalArgumentException("The skindirectory is not existing!");
Main.handleUnhandableProblem(iae);
}
cleanModel();
Skin skin = null;
skin = new Skin(Constants.RESHACKER_SCRIPT_PATH, Constants.SYSTEMFILE_AUTHUI_TMPPATH, Constants.UITEXT_TMP,
Constants.SYSTEMFILE_BASEBRD_TMPPATH);
try {
skin.load(skinPath);
{
LOGGER.debug("Skin loaded!");
LOGGER.debug(skin.toString());
}
} catch (IOException e) {
Main.handleUnhandableProblem(e);
}
return skin;
}
@Override
public boolean deleteSkin(Path skinPath) {
if (!FileUtil.control(skinPath)) {
IllegalArgumentException iae = new IllegalArgumentException("The skin is not existing!");
Main.handleUnhandableProblem(iae);
}
boolean ret = FileUtil.deleteFile(skinPath);
if (ret) {
LOGGER.debug("Skin deleted!");
LOGGER.debug(skinPath.toString());
}
return ret;
}
@Override
public boolean saveSkinToDefaultDirectory(Skin skin) {
if (skin == null) {
IllegalArgumentException iae = new IllegalArgumentException("Invalid skin!");
Main.handleUnhandableProblem(iae);
}
boolean ret = true;
try {
skin.save(Constants.PROGRAM_SKINS_PATH);
} catch (IOException e) {
Main.handleUnhandableProblem(e);
ret = false;
}
if (ret) {
skin.setSkinChanged(false); // If the skin was saved, the change status is reseted.
LOGGER.debug("Skin saved!");
LOGGER.debug(skin);
}
return ret;
}
@Override
public boolean saveAsSkinToDefaultDirectory(Skin skin, String newName) {
if (skin == null) {
IllegalArgumentException iae = new IllegalArgumentException("Invalid skin!");
LOGGER.error(iae.getMessage(), iae);
throw iae;
}
if ((newName == null) || newName.equals("")) {
IllegalArgumentException iae = new IllegalArgumentException("New name is invalid!");
LOGGER.error(iae.getMessage(), iae);
throw iae;
}
boolean ret = true;
try {
skin.setName(newName);
skin.save(Constants.PROGRAM_SKINS_PATH, newName);
} catch (IOException e) {
LOGGER.error(e);
ret = false;
}
if (ret) {
skin.setSkinChanged(false);
LOGGER.debug("Skin saved!");
LOGGER.debug(skin);
}
return ret;
}
@Override
public boolean applySkin(Path skinPath) {
boolean ret = true;
Skin tmp;
if (FileUtil.control(skinPath)) {
tmp = loadSkin(skinPath);
ret = applySkin(tmp);
tmp.shutdown();
createTemporaryData(); // Dont delete temp. There may be a skin opened.
} else {
ret = false;
}
return ret;
}
@Override
public boolean applySkin(Skin skin) {
if (skin == null) {
IllegalArgumentException iae = new IllegalArgumentException("Invalid skin!");
LOGGER.error(iae.getMessage(), iae);
throw iae;
}
boolean ret = false;
if (Settings.backUpCreated()) { // Before something in the system gets changed the backup has to be created.
if (Files.exists(Constants.SYSTEMFILE_BASEBRD_TMPPATH, LinkOption.NOFOLLOW_LINKS)
&& Files.exists(Constants.SYSTEMFILE_AUTHUI_TMPPATH, LinkOption.NOFOLLOW_LINKS)) {
try { // Apply, Kill explorer to get free access to the systemfiles and copy authui and basebrd.
skin.applyToTempAuthui();
} catch (IOException
| InterruptedException e) {
Main.handleUnhandableProblem(e);
}
ret = FileUtil.setWindowsExplorerActive(false);
if (ret) {
copyToSystemHelper(Constants.SYSTEMFILE_AUTHUI_TMPPATH, Constants.SYSTEMFILE_BASEBRD_TMPPATH);
}
FileUtil.setWindowsExplorerActive(true);
}
}
if (ret) {
LOGGER.trace("Skin applied!");
LOGGER.trace(skin);
} else {
LOGGER.trace("Skin not applied!");
}
return ret;
}
@Override
public boolean applyBackup() {
if (!Files.exists(Constants.SYSTEMFILE_AUTHUI_BAKPATH, LinkOption.NOFOLLOW_LINKS)
|| !Files.exists(Constants.SYSTEMFILE_BASEBRD_BAKPATH, LinkOption.NOFOLLOW_LINKS)) {
return false;
}
boolean ret = true;
try { //Copy all backup files in the system.
ret = FileUtil.setWindowsExplorerActive(false);
if (ret) {
copyToSystemHelper(Constants.SYSTEMFILE_AUTHUI_BAKPATH, Constants.SYSTEMFILE_BASEBRD_BAKPATH);
}
FileUtil.setWindowsExplorerActive(true);
UIBackground.disableBackgrounds(); // The logon background is not saved in the dll files.
} catch (IOException
| InterruptedException e) {
ret = false;
}
return ret;
}
// Careful, Mud ahead.
private void copyToSystemHelper(Path authui, Path basebrd) {
int MAXCOUNT = 10;
int SLEEP = 250;
boolean b;
int c = 0;
do { // Thats the first and only time that im controlling a program with exceptions, i swear! :]
try {
FileUtil.copyFile(authui, this.authuiSystemfilePath, true);
FileUtil.copyFile(basebrd, this.basebrdSystemfilePath, true);
b = true;
} catch (IOException e) {
b = false;
try {
Thread.sleep(SLEEP); //TODO Better solution
} catch (InterruptedException e1) {
;
}
LOGGER.error(e);
LOGGER.error((c + 1) + ". Try");
}
c++;
} while (!b && (c <= MAXCOUNT));
if (!b && (c > MAXCOUNT)) {
Main.handleUnhandableProblem("java.nio.file.AccessDeniedException: C:\\Windows\\System32\\authui.dll occured!",
"Apply helper method in model. It was not possible to copy the dll file.");
}
}
@Override
public List<SkinPreviewVO> getAvailableSkins() {
cleanModel();
List<SkinPreviewVO> previews = new LinkedList<SkinPreviewVO>();
try (DirectoryStream<Path> dir = Files.newDirectoryStream(Constants.PROGRAM_SKINS_PATH)) {
for (Path path : dir) {
if (path.getFileName().toString().endsWith(Constants.SKINFILE_SUFFIX)) {
Path previewPath = ZipSkinLoader.unpackFileFromSkin(path); // unpack and read the preview file.
if (previewPath != null) {
previews.add(new SkinPreviewVO(previewPath));
} else {
continue;
}
}
}
dir.close();
} catch (IOException e) {
Main.handleUnhandableProblem(e);
}
return previews;
}
/*
* Deletes all temporary files and recreates the directory structure.
*/
private Error cleanModel() {
try {
FileUtil.deleteDirectory(Constants.PROGRAM_TMP_PATH);
} catch (IOException e) {
LOGGER.warn("Dir opened by user?", e);
}
return createTemporaryData();
}
private static boolean controlBasefiles() {
boolean ret = FileUtil.controlDirectory(Constants.PROGRAM_WORKBASE_IMG_PATH);
if (ret) {
ret = FileUtil.control(Constants.EDITOR_DEFAULT_BACKGROUNDIMAGE);
}
if (ret) {
ret = FileUtil.control(Constants.EDITOR_USERIMG);
}
if (ret) {
ret = FileUtil.control(Constants.EDITOR_GUESTIMG);
}
if (ret) {
ret = FileUtil.control(Constants.SKIN_PREVIEWIMAGE_DEFAULT);
}
if (ret) {
ret = FileUtil.control(Constants.UITEXT_WORKBASE);
}
if (ret) {
int count = 0;
try {
DirectoryStream<Path> dir = Files.newDirectoryStream(Constants.PROGRAM_WORKBASE_IMG_PATH);
for (@SuppressWarnings("unused")
Path p : dir) {
count++;
}
dir.close();
} catch (IOException e) {
Main.handleUnhandableProblem(e);
}
if (count < Constants.PROGRAM_WORKBASE_IMG_COUNT) {
ret = false;
}
}
return ret;
}
private Error removeProtection() {
Error ret = null;
try {
FileUtil.createDirectory(Constants.PROGRAM_BACKUP_PATH);
if (!FileUtil.setProtection(this.authuiSystemfilePath, false) || // Make the systemfiles replaceable.
!FileUtil.setProtection(this.basebrdSystemfilePath, false)) {
ret = Error.PROTECTION_ERROR;
}
} catch (IOException e) {
ret = Error.PROTECTION_ERROR;
}
return ret;
}
private Constants.Error createTemporaryData() {
{
LOGGER.debug("Creating Temporary Data!");
}
Constants.Error ret = null;
ret = createBackUpFiles();
if (ret == null) {
try {
FileUtil.createDirectory(Constants.PROGRAM_TMP_PATH);
FileUtil.createDirectory(Constants.PROGRAM_TMPSKIN_PATH);
FileUtil.createDirectory(Constants.PROGRAM_SKINS_PATH);
FileUtil.copyFile(Constants.SYSTEMFILE_AUTHUI_BAKPATH, Constants.SYSTEMFILE_AUTHUI_TMPPATH, true);
FileUtil.copyFile(Constants.SYSTEMFILE_BASEBRD_BAKPATH, Constants.SYSTEMFILE_BASEBRD_TMPPATH, true);
if (Files.exists(Constants.SYSTEMFILE_AUTHUI_TMPPATH, LinkOption.NOFOLLOW_LINKS)
&& Files.exists(Constants.SYSTEMFILE_BASEBRD_TMPPATH, LinkOption.NOFOLLOW_LINKS)) {
FileUtil.copyFile(Constants.UITEXT_WORKBASE, Constants.UITEXT_TMP, false);
if (!FileUtil.control(Constants.UITEXT_TMP)) {
ret = Error.COPIED_UIFILE_NOT_AVAILABLE;
}
}
} catch (IOException e) {
ret = Error.TEMP_CREATION_ERROR;
}
}
return ret;
}
private Constants.Error createBackUpFiles() {
Constants.Error ret = null;
try { // Copy the unchanged systemfile in the backup directory. If the files already exist but the program has no info about it, it
// saves a second copy and changes the name of the existing file to a name with date.
if (!Files.exists(Constants.SYSTEMFILE_AUTHUI_BAKPATH, LinkOption.NOFOLLOW_LINKS)) {
FileUtil.copyFile(this.authuiSystemfilePath, Constants.SYSTEMFILE_AUTHUI_BAKPATH, true);
} else {
if (!Settings.backUpCreated()) {
String tmp = new java.util.Date().toString();
tmp = tmp.replace(":", "-");
String tmp2 = Constants.SYSTEMFILE_AUTHUI_BAKPATH.getFileName().toString().replace(".dll", "_" + tmp + ".dll");
FileUtil.moveFile(Constants.SYSTEMFILE_AUTHUI_BAKPATH, Constants.PROGRAM_BACKUP_PATH.resolve(tmp2));
FileUtil.copyFile(this.authuiSystemfilePath, Constants.SYSTEMFILE_AUTHUI_BAKPATH, true);
}
}
if (!Files.exists(Constants.SYSTEMFILE_BASEBRD_BAKPATH, LinkOption.NOFOLLOW_LINKS)) {
FileUtil.copyFile(this.basebrdSystemfilePath, Constants.SYSTEMFILE_BASEBRD_BAKPATH, true);
} else {
if (!Settings.backUpCreated()) {
String tmp = new java.util.Date().toString();
tmp = tmp.replace(":", "-");
String tmp2 = Constants.SYSTEMFILE_BASEBRD_BAKPATH.getFileName().toString()
.replace(".dll", "_" + tmp + ".dll");
FileUtil.moveFile(Constants.SYSTEMFILE_BASEBRD_BAKPATH, Constants.PROGRAM_BACKUP_PATH.resolve(tmp2));
FileUtil.copyFile(this.basebrdSystemfilePath, Constants.SYSTEMFILE_BASEBRD_BAKPATH, true);
}
}
} catch (IOException e) {
ret = Error.BACKUP_CREATION_ERROR;
}
// control if the backup is really created.
if (!Files.exists(Constants.SYSTEMFILE_AUTHUI_BAKPATH, LinkOption.NOFOLLOW_LINKS)
|| !Files.exists(Constants.SYSTEMFILE_BASEBRD_BAKPATH, LinkOption.NOFOLLOW_LINKS)) {
ret = Error.BACKUP_CREATION_ERROR;
Settings.setBackUpCreated(false);
} else {
Settings.setBackUpCreated(true);
}
return ret;
}
}