Skip to content
Snippets Groups Projects
Commit 9b1032fe authored by Matyáš Latner's avatar Matyáš Latner
Browse files

#14 Assets directory moved from ".tablexia" to ".tablexia/assets". Data...

#14 Assets directory moved from ".tablexia" to ".tablexia/assets". Data directory moved from ".tablexia" to ".tablexia/data"

 * refactored mechanism of files access
 * new utility methods for file access
parent 7d1a15a6
No related branches found
No related tags found
No related merge requests found
Showing
with 197 additions and 74 deletions
......@@ -111,17 +111,7 @@ tasks.whenTaskAdded { compileTask ->
File buildConfigFile = iter.next()
String buildConfigContent = buildConfigFile.getText(BUILD_CONFIG_ENCODING)
/*
String checksums = "";
rootProject.ext.assetsChecksum.each { key, value ->
if (checksums.length() > 0) {
checksums = checksums + ", ";
}
checksums = checksums + "\"${key}\", \"${value}\""
}
*/
buildConfigContent = buildConfigContent.replace(rootProject.ext.assetsChecksumPattern, '\"' + rootProject.ext.assetsChecksum.toString() + '\"')
buildConfigContent = buildConfigContent.replace(rootProject.ext.assetsChecksumPattern, '\"' + getMapConvertedToString(rootProject.ext.assetsChecksum) + '\"')
buildConfigFile.write(buildConfigContent, BUILD_CONFIG_ENCODING)
}
}
......
......@@ -21,7 +21,7 @@ public class AndroidLauncher extends AndroidApplication {
getResources().getConfiguration().locale,
BuildConfig.VERSION_NAME,
SQL_CONNECTION_TYPE,
Utility.createChecksumMapFromString(BuildConfig.ASSETS_CHECKSUMS, ":"),
Utility.createChecksumMapFromString(BuildConfig.ASSETS_CHECKSUMS),
savedInstanceState == null), config);
}
}
......@@ -68,6 +68,15 @@ def getVersionCodeFromGit() {
return stdout.toString().trim().toInteger()
}
def getMapConvertedToString(Map map) {
String result = "";
map.each { key, value ->
if (result.size() > 0) result = result + ","
result = result + "$key:$value"
}
return result;
}
task zipAssets(type:Zip) {
outputs.upToDateWhen { false }
new File(project(":core").projectDir.absolutePath + "/assets").eachDir() { dir ->
......
package cz.nic.tablexia;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.util.Log;
/**
......@@ -15,8 +15,8 @@ import cz.nic.tablexia.util.Log;
*/
public class TablexiaStorage {
public static final String DATABASE_DIRECTORY = Gdx.files.getExternalStoragePath() + TablexiaAssetManager.StorageType.EXTERNAL.getStoragePath();
public static final String DATABASE_NAME = "tablexia.db";
public static final FileHandle DATABASE_DIRECTORY = TablexiaAbstractFileManager.getFileStoragePathFileHandle(TablexiaAbstractFileManager.DataStorageType.EXTERNAL);
public static final String DATABASE_NAME = "tablexia.db";
private static TablexiaStorage instance = null;
private Connection connection = null;
......@@ -27,9 +27,12 @@ public class TablexiaStorage {
static void init(Tablexia.SQLConnectionType connectionType) {
if (instance == null) {
if (!DATABASE_DIRECTORY.exists()) {
DATABASE_DIRECTORY.mkdirs();
}
try {
Class.forName(connectionType.getDriver());
Connection connection = DriverManager.getConnection(connectionType.getConnectionString() + DATABASE_DIRECTORY + "/" + DATABASE_NAME);
Connection connection = DriverManager.getConnection(connectionType.getConnectionString() + DATABASE_DIRECTORY.file().getAbsolutePath() + "/" + DATABASE_NAME);
instance = new TablexiaStorage(connection);
} catch (ClassNotFoundException e) {
Log.err(TablexiaStorage.class, "Cannot find class for database driver!", e);
......
......@@ -4,20 +4,28 @@ import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.ExternalFileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import cz.nic.tablexia.util.Log;
public class TablexiaAssetManager extends AssetManager {
public abstract class TablexiaAbstractFileManager extends AssetManager {
public enum StorageType {
public interface StorageType {
String getStoragePath();
FileHandleResolver getResolver();
}
public enum RootStorageType implements StorageType {
INTERNAL("", new InternalFileHandleResolver()),
EXTERNAL(".tablexia/", new ExternalFileHandleResolver());
EXTERNAL(RootStorageType.TABLEXIA_DIRECTORY, new ExternalFileHandleResolver());
public static final String TABLEXIA_DIRECTORY = ".tablexia/";
private String storagePath;
private FileHandleResolver resolver;
StorageType(String storagePath, FileHandleResolver resolver) {
RootStorageType(String storagePath, FileHandleResolver resolver) {
this.storagePath = storagePath;
this.resolver = resolver;
}
......@@ -31,9 +39,73 @@ public class TablexiaAssetManager extends AssetManager {
}
}
private final StorageType storageType;
public enum AssetsStorageType implements StorageType {
INTERNAL(RootStorageType.INTERNAL, ""),
EXTERNAL(RootStorageType.EXTERNAL, AssetsStorageType.ASSETS_DIRECTORY);
public static final String ASSETS_DIRECTORY = "assets/";
private String storagePath;
private FileHandleResolver resolver;
AssetsStorageType(RootStorageType rootStorageType, String assetsPath) {
this.storagePath = rootStorageType.getStoragePath() + assetsPath;
this.resolver = rootStorageType.getResolver();
}
public String getStoragePath() {
return storagePath;
}
public FileHandleResolver getResolver() {
return resolver;
}
}
public enum DataStorageType implements StorageType {
INTERNAL(RootStorageType.INTERNAL, DataStorageType.DATA_DIRECTORY),
EXTERNAL(RootStorageType.EXTERNAL, DataStorageType.DATA_DIRECTORY);
public static final String DATA_DIRECTORY = "data/";
private String storagePath;
private FileHandleResolver resolver;
DataStorageType(RootStorageType rootStorageType, String assetsPath) {
this.storagePath = rootStorageType.getStoragePath() + assetsPath;
this.resolver = rootStorageType.getResolver();
}
public String getStoragePath() {
return storagePath;
}
public FileHandleResolver getResolver() {
return resolver;
}
}
public static FileHandle getFileStoragePathFileHandle(StorageType storageType, String additionalPath) {
return storageType.getResolver().resolve(storageType.getStoragePath() + additionalPath);
}
public static FileHandle getFileStoragePathFileHandle(StorageType storageType) {
return getFileStoragePathFileHandle(storageType, "");
}
public static String getFileStoragePath(StorageType storageType, String additionalPath) {
return getFileStoragePathFileHandle(storageType, additionalPath).file().getAbsolutePath();
}
public static String getFileStoragePath(StorageType storageType) {
return getFileStoragePathFileHandle(storageType).file().getAbsolutePath();
}
private final StorageType storageType;
protected TablexiaAssetManager(StorageType storageType) {
public TablexiaAbstractFileManager(StorageType storageType) {
super(storageType.getResolver());
this.storageType = storageType;
}
......
......@@ -2,9 +2,13 @@ package cz.nic.tablexia.loader;
import com.badlogic.gdx.audio.Sound;
public class TablexiaSoundManager extends TablexiaAssetManager {
public class TablexiaSoundManager extends TablexiaAbstractFileManager {
public TablexiaSoundManager(StorageType storageType) {
public TablexiaSoundManager() {
super(AssetsStorageType.EXTERNAL);
}
public TablexiaSoundManager(AssetsStorageType storageType) {
super(storageType);
}
......
......@@ -2,9 +2,13 @@ package cz.nic.tablexia.loader;
import com.badlogic.gdx.graphics.Texture;
public class TablexiaTextureManager extends TablexiaAssetManager {
public class TablexiaTextureManager extends TablexiaAbstractFileManager {
public TablexiaTextureManager(StorageType storageType) {
public TablexiaTextureManager() {
super(AssetsStorageType.EXTERNAL);
}
public TablexiaTextureManager(AssetsStorageType storageType) {
super(storageType);
}
......
......@@ -8,9 +8,7 @@ public class ApplicationSoundManager extends TablexiaSoundManager implements IAp
private static ApplicationSoundManager instance;
private ApplicationSoundManager() {
super(StorageType.EXTERNAL);
}
private ApplicationSoundManager() {}
public static ApplicationSoundManager getInstance() {
if (instance == null) {
......@@ -25,11 +23,11 @@ public class ApplicationSoundManager extends TablexiaSoundManager implements IAp
instance = null;
}
private static final String APPLICATION_PATH = "application/";
private static final String APPLICATION_PATH = "application/";
private static final String MAINMENU_PATH = APPLICATION_PATH + "mainmenu/";
public static final String MAINMENU_OPEN = MAINMENU_PATH + "mainmenu_open.mp3";
public static final String MAINMENU_CLOSE = MAINMENU_PATH + "mainmenu_close.mp3";
private static final String MAINMENU_PATH = APPLICATION_PATH + "mainmenu/";
public static final String MAINMENU_OPEN = MAINMENU_PATH + "mainmenu_open.mp3";
public static final String MAINMENU_CLOSE = MAINMENU_PATH + "mainmenu_close.mp3";
public void load() {
loadSound(MAINMENU_OPEN);
......
......@@ -8,9 +8,7 @@ public class ApplicationTextureManager extends TablexiaTextureManager implements
private static ApplicationTextureManager instance;
private ApplicationTextureManager() {
super(StorageType.EXTERNAL);
}
private ApplicationTextureManager() {}
public static ApplicationTextureManager getInstance() {
if (instance == null) {
......@@ -29,10 +27,10 @@ public class ApplicationTextureManager extends TablexiaTextureManager implements
public static final String BACKGROUND_WOODEN = "universal/background_wooden.png";
}
private static final String APPLICATION_PATH = "application/";
private static final String APPLICATION_PATH = "application/";
private static final String MAINMENU_PATH = APPLICATION_PATH + "mainmenu/";
public static final String MAINMENU_BACKGROUND = MAINMENU_PATH + "background.png";
private static final String MAINMENU_PATH = APPLICATION_PATH + "mainmenu/";
public static final String MAINMENU_BACKGROUND = MAINMENU_PATH + "background.png";
public void load() {
loadTexture(MAINMENU_BACKGROUND);
......
package cz.nic.tablexia.loader.zip;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.async.AsyncTask;
......@@ -17,7 +16,7 @@ import java.util.zip.ZipInputStream;
import cz.nic.tablexia.checksum.Checksum;
import cz.nic.tablexia.loader.IApplicationLoader;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.loader.TablexiaDataManager;
import cz.nic.tablexia.util.Log;
......@@ -29,13 +28,13 @@ import cz.nic.tablexia.util.Log;
*/
public class ZipAssetLoader extends TablexiaDataManager<Void> implements IApplicationLoader {
public static final TablexiaAssetManager.StorageType ZIP_FILES_STORAGE_TYPE = TablexiaAssetManager.StorageType.INTERNAL;
public static final String ZIP_FILE_EXTENSION = ".zip";
public static final String COMMON_ZIP_FILE_NAME = "common";
public static final TablexiaAbstractFileManager.RootStorageType ZIP_FILES_STORAGE_TYPE = TablexiaAbstractFileManager.RootStorageType.INTERNAL;
public static final String ZIP_FILE_EXTENSION = ".zip";
public static final String COMMON_ZIP_FILE_NAME = "common";
private static class ZipAssetLoaderTask implements AsyncTask<Void> {
private Locale locale;
private Locale locale;
private Map<String, String> buildChecksum;
public ZipAssetLoaderTask(Locale locale, Map<String, String> buildChecksum) {
......@@ -66,14 +65,14 @@ public class ZipAssetLoader extends TablexiaDataManager<Void> implements IApplic
String commonZipFile = COMMON_ZIP_FILE_NAME + ZIP_FILE_EXTENSION;
String localisedZipFile = language + ZIP_FILE_EXTENSION;
String extractDestinationDirectory = Gdx.files.getExternalStoragePath() + TablexiaAssetManager.StorageType.EXTERNAL.getStoragePath();
String extractDestinationDirectory = TablexiaAbstractFileManager.getFileStoragePath(TablexiaAbstractFileManager.AssetsStorageType.EXTERNAL);
if (buildChecksum == null || !checkAssets(buildChecksum.get(language), extractDestinationDirectory)) {
Log.info(getClass(), "Assets check FAILED! --> Extracting new assets");
Log.info(getClass(), "Assets check FAILED! --> Extracting new assets to: " + extractDestinationDirectory);
unzip(ZIP_FILES_STORAGE_TYPE.getResolver().resolve(ZIP_FILES_STORAGE_TYPE.getStoragePath() + commonZipFile), extractDestinationDirectory);
unzip(ZIP_FILES_STORAGE_TYPE.getResolver().resolve(ZIP_FILES_STORAGE_TYPE.getStoragePath() + localisedZipFile), extractDestinationDirectory);
} else {
Log.info(getClass(), "Assets check OK!");
Log.info(getClass(), "Assets check OK! --> Continue to loading");
}
return null;
......
......@@ -7,7 +7,7 @@ import com.badlogic.gdx.graphics.Texture;
import java.util.List;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.util.assetmanager.DirectoryAsset;
/**
......@@ -30,7 +30,7 @@ public class AbstractAutoloadTablexiaScreen<T> extends AbstractLinearTextureTabl
initAssets(basePath);
}
public AbstractAutoloadTablexiaScreen(boolean hasState, boolean loadAsync, TablexiaAssetManager.StorageType storageType, String basePath) {
public AbstractAutoloadTablexiaScreen(boolean hasState, boolean loadAsync, TablexiaAbstractFileManager.AssetsStorageType storageType, String basePath) {
super(hasState, loadAsync, storageType);
initAssets(basePath);
}
......@@ -70,6 +70,6 @@ public class AbstractAutoloadTablexiaScreen<T> extends AbstractLinearTextureTabl
}
public Music getMusic(String musicName) {
return Gdx.audio.newMusic(Gdx.files.external(TablexiaAssetManager.StorageType.EXTERNAL.getStoragePath() + musicAssets.get(musicName)));
return Gdx.audio.newMusic(Gdx.files.external(TablexiaAbstractFileManager.RootStorageType.EXTERNAL.getStoragePath() + musicAssets.get(musicName)));
}
}
......@@ -2,7 +2,7 @@ package cz.nic.tablexia.screen;
import com.badlogic.gdx.graphics.Texture;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
/**
* Created by lhoracek on 4/8/15.
......@@ -11,7 +11,7 @@ public class AbstractLinearTextureTablexiaScreen<T> extends AbstractTablexiaScre
public AbstractLinearTextureTablexiaScreen() {
}
public AbstractLinearTextureTablexiaScreen(boolean hasState, boolean loadAsync, TablexiaAssetManager.StorageType storageType) {
public AbstractLinearTextureTablexiaScreen(boolean hasState, boolean loadAsync, TablexiaAbstractFileManager.AssetsStorageType storageType) {
super(hasState, loadAsync, storageType);
}
......
......@@ -22,7 +22,7 @@ import cz.nic.tablexia.TablexiaSettings;
import cz.nic.tablexia.bus.ApplicationBus;
import cz.nic.tablexia.bus.ApplicationBus.ApplicationEvent;
import cz.nic.tablexia.bus.event.MainMenuPositionChangedEvent;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.loader.TablexiaDataManager;
import cz.nic.tablexia.loader.TablexiaSoundManager;
import cz.nic.tablexia.loader.TablexiaTextureManager;
......@@ -112,10 +112,10 @@ public abstract class AbstractTablexiaScreen<T> extends ScreenAdapter {
public AbstractTablexiaScreen() {
this(true, true, TablexiaAssetManager.StorageType.EXTERNAL);
this(true, true, TablexiaAbstractFileManager.AssetsStorageType.EXTERNAL);
}
public AbstractTablexiaScreen(boolean hasState, boolean loadAsync, TablexiaAssetManager.StorageType storageType) {
public AbstractTablexiaScreen(boolean hasState, boolean loadAsync, TablexiaAbstractFileManager.AssetsStorageType storageType) {
stage = prepareStage();
this.hasState = hasState;
this.loadAsync = loadAsync;
......
......@@ -4,14 +4,10 @@ import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import cz.nic.tablexia.TablexiaSettings;
import cz.nic.tablexia.TablexiaStorage;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.screen.AbstractLinearTextureTablexiaScreen;
public class LoaderScreen extends AbstractLinearTextureTablexiaScreen<Void> {
......@@ -21,7 +17,7 @@ public class LoaderScreen extends AbstractLinearTextureTablexiaScreen<Void> {
private static final String LOADER_BIG_HAND = "gfx/screen_loader_bighand.png";
public LoaderScreen() {
super(false, false, TablexiaAssetManager.StorageType.INTERNAL);
super(false, false, TablexiaAbstractFileManager.AssetsStorageType.INTERNAL);
}
@Override
......
......@@ -6,7 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image;
import java.util.List;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.screen.AbstractTablexiaScreen;
public class FullscreenImageDebugScreen extends AbstractTablexiaScreen<Void> {
......@@ -16,7 +16,7 @@ public class FullscreenImageDebugScreen extends AbstractTablexiaScreen<Void> {
private static final String LOADER_BIG_HAND = "gfx/screen_loader_bighand.png";
public FullscreenImageDebugScreen() {
super(false, false, TablexiaAssetManager.StorageType.INTERNAL);
super(false, false, TablexiaAbstractFileManager.AssetsStorageType.INTERNAL);
}
@Override
......
......@@ -19,12 +19,11 @@ public class Utility {
return null;
}
public static Map<String, String> createChecksumMapFromString(String checksumsString, String keyValueSeparator) {
if (checksumsString == null) {
public static Map<String, String> createChecksumMapFromString(String checksums) {
if (checksums == null) {
return null;
}
String checksums = checksumsString.substring(1, checksumsString.length() - 1);
return Splitter.on(',').trimResults().withKeyValueSeparator(keyValueSeparator).split(checksums);
return Splitter.on(',').trimResults().withKeyValueSeparator(":").split(checksums);
}
}
package cz.nic.tablexia.util.assetmanager;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import java.util.HashMap;
import cz.nic.tablexia.loader.TablexiaAssetManager;
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;
import cz.nic.tablexia.util.Log;
/**
......@@ -27,7 +26,7 @@ public class DirectoryAsset extends HashMap<String, String> {
private void loadFiles(String path, FileHandle fh) {
if (fh.isDirectory()) {
Log.info(getClass().getName(), "Is directory: " + fh.name());
Log.debug(getClass(), "Is directory: " + fh.name());
for (FileHandle entry : fh.list()) {
loadFiles(path + fh.name() + "/", entry);
}
......@@ -38,14 +37,14 @@ public class DirectoryAsset extends HashMap<String, String> {
private FileHandle getDirHandle(String path) {
FileHandle dirHandle;
dirHandle = Gdx.files.external(TablexiaAssetManager.StorageType.EXTERNAL.getStoragePath() + path);
dirHandle = TablexiaAbstractFileManager.getFileStoragePathFileHandle(TablexiaAbstractFileManager.AssetsStorageType.EXTERNAL, path);
return dirHandle;
}
public String add(String path, FileHandle fh) {
String fullPath = basePath + "/" + path + fh.name();
String key = path + fh.nameWithoutExtension();
Log.info(getClass().getName(), "Adding asset: " + key + " : " + fullPath);
Log.debug(getClass(), "Adding asset: " + key + " : " + fullPath);
return super.put(key, fullPath);
}
......@@ -53,7 +52,7 @@ public class DirectoryAsset extends HashMap<String, String> {
public String get(Object key) {
String value = super.get(key);
if(value == null){
Log.info(getClass().getName(), "Asset not prepared: " + key);
Log.err(getClass(), "Asset not prepared: " + key);
throw new IllegalArgumentException("Asset not prepared: " + key);
}
return value;
......
......@@ -18,7 +18,7 @@ public class XFillViewport extends Viewport {
public XFillViewport() {
setWorldSize(TablexiaSettings.getWorldWidth(), TablexiaSettings.getMinWorldHeight());
setCamera(new OrthographicCamera());
Log.info(getClass().getName(), "Viewport size: " + TablexiaSettings.getWorldWidth() + "x" + TablexiaSettings.getMinWorldHeight());
Log.info(getClass(), "Viewport size: " + TablexiaSettings.getWorldWidth() + "x" + TablexiaSettings.getMinWorldHeight());
}
private float cameraYOffset;
......@@ -45,7 +45,7 @@ public class XFillViewport extends Viewport {
@Override
public void apply(boolean centerCamera) {
Log.info(getClass().getName(), "New viewport size (min: " + TablexiaSettings.getMinWorldHeight() + ") position: " + getScreenX() + ":" + getScreenY() + " - bounds: " + getScreenWidth() + ":" + getScreenHeight() + " - world: " + getWorldWidth() + "x" + getWorldHeight() + " - y offset: " + cameraYOffset);
Log.info(getClass(), "New viewport size (min: " + TablexiaSettings.getMinWorldHeight() + ") position: " + getScreenX() + ":" + getScreenY() + " - bounds: " + getScreenWidth() + ":" + getScreenHeight() + " - world: " + getWorldWidth() + "x" + getWorldHeight() + " - y offset: " + cameraYOffset);
Gdx.gl.glViewport(getScreenX(), getScreenY(), getScreenWidth(), getScreenHeight());
getCamera().viewportWidth = getWorldWidth();
......
......@@ -26,7 +26,7 @@ task debugJar(type: Jar) {
attributes 'Main-Class': project.mainClassName
attributes 'Build-Type': 'debug'
attributes 'Version-Name': tablexiaVersionName
attributes 'Assets-Cheksums': rootProject.ext.assetsChecksum.toString()
attributes 'Assets-Cheksums': getMapConvertedToString(rootProject.ext.assetsChecksum)
}
}
}
......@@ -43,7 +43,7 @@ task releaseJar(type: Jar) {
attributes 'Main-Class': project.mainClassName
attributes 'Build-Type': 'release'
attributes 'Version-Name': tablexiaVersionName
attributes 'Assets-Cheksums': rootProject.ext.assetsChecksum
attributes 'Assets-Cheksums': getMapConvertedToString(rootProject.ext.assetsChecksum)
}
}
}
......
......@@ -59,7 +59,7 @@ public class DesktopLauncher {
Locale.getDefault(),
versionName,
SQL_CONNECTION_TYPE,
Utility.createChecksumMapFromString(checksums, "="),
Utility.createChecksumMapFromString(checksums),
true), config);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment