Refactor cards and token images code (#10139):

- removed outdated token store format (example: BIRD.W.BIRD.CREATURE.1.1.full.jpg)
 - removed duplicated cache images code;
 - removed duplicated set code fields;
 - removed outdated auto-generated thumb files (also all *.thumb.zip files will be deleted on startup);
This commit is contained in:
Oleg Agafonov 2023-03-24 06:44:11 +04:00
parent d93e533c75
commit 66d0ef4b35
25 changed files with 333 additions and 634 deletions

View file

@ -293,7 +293,11 @@ public class CardIconsPanel extends JPanel {
//BufferedImage iconImage = ImageManagerImpl.instance.getCardIcon(icon.getIconType().getResourceName(), this.halfSize * 2);
// cached call
BufferedImage iconImageCached = ImageCache.getCardIconImage(icon.getIconType().getResourceName(), this.halfSize * 2, color.toString());
BufferedImage iconImageCached = ImageCache.getCardIconImage(
icon.getIconType().getResourceName(),
this.halfSize * 2,
color.toString()
).getImage();
if (iconImageCached != null && this.font != null) {
BufferedImage iconImageWithText = ImageManagerImpl.deepCopy(iconImageCached); // must copy cached value before modify

View file

@ -51,7 +51,6 @@ public final class Constants {
// cards render
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
public static final Rectangle THUMBNAIL_SIZE_FULL = new Rectangle(102, 146);
// resources - default images
public static final String RESOURCE_PATH_DEFAULT_IMAGES = File.separator + "default";

View file

@ -677,7 +677,7 @@ public class MageActionCallback implements ActionCallback {
switch (enlargeMode) {
case COPY:
if (cardView instanceof PermanentView) {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal());
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()).getImage();
}
break;
case ALTERNATE:
@ -686,9 +686,9 @@ public class MageActionCallback implements ActionCallback {
&& !cardView.isFlipCard()
&& !cardView.canTransform()
&& ((PermanentView) cardView).isCopy()) {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal());
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()).getImage();
} else {
image = ImageCache.getImageOriginalAlternateName(cardView);
image = ImageCache.getImageOriginalAlternateName(cardView).getImage();
displayCard = displayCard.getSecondCardFace();
}
}

View file

@ -3,7 +3,6 @@ package org.mage.card.arcane;
import mage.MageInt;
import mage.cards.MageCardLocation;
import mage.cards.action.ActionCallback;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.ImageCaches;
import mage.client.util.ImageHelper;
@ -19,6 +18,7 @@ import mage.view.PermanentView;
import mage.view.StackAbilityView;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.images.ImageCacheData;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import javax.swing.*;
@ -466,9 +466,9 @@ public class CardPanelRenderModeImage extends CardPanel {
public Image getImage() {
if (this.hasImage) {
if (getGameCard().isFaceDown()) {
return getFaceDownImage();
return getFaceDownImage().getImage();
} else {
return ImageCache.getImageOriginal(getGameCard());
return ImageCache.getImageOriginal(getGameCard()).getImage();
}
}
return null;
@ -629,27 +629,27 @@ public class CardPanelRenderModeImage extends CardPanel {
setTappedAngle(isTapped() ? CardPanel.TAPPED_ANGLE : 0);
setFlippedAngle(isFlipped() ? CardPanel.FLIPPED_ANGLE : 0);
//final CardView gameCard = this.gameCard;
final int stamp = ++updateArtImageStamp;
Util.threadPool.submit(() -> {
try {
final BufferedImage srcImage;
final ImageCacheData data;
if (getGameCard().isFaceDown()) {
srcImage = getFaceDownImage();
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
data = getFaceDownImage();
} else {
srcImage = ImageCache.getThumbnail(getGameCard());
data = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
}
if (srcImage == null) {
setFullPath(ImageCache.getFilePath(getGameCard(), getCardWidth()));
// show path on miss image
if (data.getImage() == null) {
setFullPath(data.getPath());
}
UI.invokeLater(() -> {
if (stamp == updateArtImageStamp) {
hasImage = srcImage != null;
hasImage = data.getImage() != null;
setTitle(getGameCard());
setImage(srcImage);
setImage(data.getImage());
}
});
} catch (Exception | Error e) {
@ -658,7 +658,7 @@ public class CardPanelRenderModeImage extends CardPanel {
});
}
private BufferedImage getFaceDownImage() {
private ImageCacheData getFaceDownImage() {
// TODO: add download default images
if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) {

View file

@ -3,7 +3,6 @@ package org.mage.card.arcane;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import mage.cards.action.ActionCallback;
import mage.client.constants.Constants;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
@ -13,6 +12,7 @@ import mage.view.PermanentView;
import mage.view.StackAbilityView;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.images.ImageCacheData;
import java.awt.*;
import java.awt.image.BufferedImage;
@ -194,9 +194,9 @@ public class CardPanelRenderModeMTGO extends CardPanel {
return null;
}
if (getGameCard().isFaceDown()) {
return getFaceDownImage();
return getFaceDownImage().getImage();
} else {
return ImageCache.getImageOriginal(getGameCard());
return ImageCache.getImageOriginal(getGameCard()).getImage();
}
}
@ -319,12 +319,9 @@ public class CardPanelRenderModeMTGO extends CardPanel {
// Nothing to do
srcImage = null;
faceArtSrcImage = null;
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight());
} else {
srcImage = ImageCache.getThumbnail(getGameCard());
faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight());
srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight()).getImage();
faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight()).getImage();
}
UI.invokeLater(() -> {
@ -352,7 +349,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
return new CardPanelAttributes(getCardWidth(), getCardHeight(), isChoosable(), isSelected(), isTransformed());
}
private BufferedImage getFaceDownImage() {
private ImageCacheData getFaceDownImage() {
// TODO: add download default images
if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) {

View file

@ -710,6 +710,6 @@ public class CardPluginImpl implements CardPlugin {
@Override
public BufferedImage getOriginalImage(CardView card) {
return ImageCache.getImageOriginal(card);
return ImageCache.getImageOriginal(card).getImage();
}
}

View file

@ -2,7 +2,6 @@ package org.mage.plugins.card.images;
import mage.util.CardUtil;
import java.util.Locale;
import java.util.Objects;
/**
@ -14,8 +13,6 @@ public class CardDownloadData {
private String downloadName;
private String fileName = "";
private String set;
private String tokenSetCode;
private String tokenDescriptor;
private final String collectorId;
private final Integer type;
private boolean token;
@ -28,55 +25,51 @@ public class CardDownloadData {
private String tokenClassName;
private boolean isType2;
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, false, "");
public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type) {
this(name, setCode, collectorId, usesVariousArt, type, false, "");
}
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, false, false, "");
public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token) {
this(name, setCode, collectorId, usesVariousArt, type, token, false, false, "");
}
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, String fileName) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, false, false, "");
public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token, String fileName) {
this(name, setCode, collectorId, usesVariousArt, type, token, false, false, "");
this.fileName = fileName;
}
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, twoFacedCard, secondSide, "");
public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token, boolean twoFacedCard, boolean secondSide) {
this(name, setCode, collectorId, usesVariousArt, type, token, twoFacedCard, secondSide, "");
}
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) {
public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) {
this.name = name;
this.set = set;
this.set = setCode;
this.collectorId = collectorId;
this.usesVariousArt = usesVariousArt;
this.type = type;
this.token = token;
this.twoFacedCard = twoFacedCard;
this.secondSide = secondSide;
this.tokenSetCode = tokenSetCode;
this.tokenDescriptor = tokenDescriptor;
this.tokenClassName = tokenClassName;
if (this.tokenDescriptor == null || this.tokenDescriptor.equalsIgnoreCase("")) {
this.tokenDescriptor = lastDitchTokenDescriptor();
}
}
public CardDownloadData(final CardDownloadData card) {
this.name = card.name;
this.downloadName = card.downloadName;
this.fileName = card.fileName;
this.set = card.set;
this.collectorId = card.collectorId;
this.type = card.type;
this.token = card.token;
this.twoFacedCard = card.twoFacedCard;
this.secondSide = card.secondSide;
this.type = card.type;
this.flipCard = card.flipCard;
this.flippedSide = card.flippedSide;
this.splitCard = card.splitCard;
this.usesVariousArt = card.usesVariousArt;
this.tokenSetCode = card.tokenSetCode;
this.tokenDescriptor = card.tokenDescriptor;
this.tokenClassName = card.tokenClassName;
this.fileName = card.fileName;
this.isType2 = card.isType2;
}
@Override
@ -94,7 +87,7 @@ public class CardDownloadData {
if (!Objects.equals(this.set, other.set)) {
return false;
}
if (!Objects.equals(this.collectorId, other.collectorId) && (this.collectorId == null || !this.collectorId.equals(other.collectorId))) {
if (!Objects.equals(this.collectorId, other.collectorId)) {
return false;
}
if (this.token != other.token) {
@ -123,6 +116,11 @@ public class CardDownloadData {
return hash;
}
@Override
public String toString() {
return String.format("%s - %s", this.getSet(), this.getName());
}
public String getCollectorId() {
return collectorId;
}
@ -171,18 +169,6 @@ public class CardDownloadData {
this.set = set;
}
public String getTokenSetCode() {
return tokenSetCode;
}
public void setTokenSetCode(String tokenSetCode) {
this.tokenSetCode = tokenSetCode;
}
public String getTokenDescriptor() {
return tokenDescriptor;
}
public void setTokenClassName(String tokenClassName) {
this.tokenClassName = tokenClassName;
}
@ -191,17 +177,6 @@ public class CardDownloadData {
return tokenClassName;
}
public void setTokenDescriptor(String tokenDescriptor) {
this.tokenDescriptor = tokenDescriptor;
}
private String lastDitchTokenDescriptor() {
String tmpName = this.name.replaceAll("[^a-zA-Z0-9]", "");
String descriptor = tmpName + "....";
descriptor = descriptor.toUpperCase(Locale.ENGLISH);
return descriptor;
}
public boolean isToken() {
return token;
}

View file

@ -444,7 +444,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) {
String cardName = card.getName();
boolean isType2 = type2SetsFilter.contains(card.getSetCode());
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), card.isNightCard());
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, false, card.isDoubleFaced(), card.isNightCard());
// variations must have diff file names with additional postfix
if (url.getUsesVariousArt()) {
@ -475,7 +475,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(),
secondSideCard.getCardNumber(),
card.usesVariousArt(),
0, "", "", false, card.isDoubleFaced(), true);
0, false, card.isDoubleFaced(), true);
url.setType2(isType2);
allCardsUrls.add(url);
}
@ -488,7 +488,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(),
card.getCardNumber(),
card.usesVariousArt(),
0, "", "", false, card.isDoubleFaced(), card.isNightCard());
0, false, card.isDoubleFaced(), card.isNightCard());
cardDownloadData.setFlipCard(true);
cardDownloadData.setFlippedSide(true);
cardDownloadData.setType2(isType2);
@ -510,7 +510,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(),
meldsToCard.getCardNumber(),
card.usesVariousArt(),
0, "", "", false, false, false);
0, false, false, false);
url.setType2(isType2);
allCardsUrls.add(url);
}
@ -523,7 +523,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(),
card.getCardNumber(),
card.usesVariousArt(),
0, "", "", false, true, true);
0, false, true, true);
cardDownloadData.setType2(isType2);
allCardsUrls.add(cardDownloadData);
}
@ -549,7 +549,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
cardsToDownload.add(card);
} else {
// need missing cards
File file = new TFile(CardImageUtils.buildImagePathToCard(card));
File file = new TFile(CardImageUtils.buildImagePathToCardOrToken(card));
if (!file.exists()) {
cardsToDownload.add(card);
}
@ -592,32 +592,32 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("TOK:")) {
String set = params[2].substring(4);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type,true);
card.setTokenClassName(tokenClassName);
list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM:")) {
String set = params[2].substring(7);
CardDownloadData card = new CardDownloadData("Emblem " + params[3], set, "0", false, type, "", "", true, fileName);
CardDownloadData card = new CardDownloadData("Emblem " + params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName);
list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM-:")) {
String set = params[2].substring(8);
CardDownloadData card = new CardDownloadData(params[3] + " Emblem", set, "0", false, type, "", "", true, fileName);
CardDownloadData card = new CardDownloadData(params[3] + " Emblem", set, "0", false, type,true, fileName);
card.setTokenClassName(tokenClassName);
list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM!:")) {
String set = params[2].substring(8);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true, fileName);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName);
list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("PLANE:")) {
String set = params[2].substring(6);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true, fileName);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName);
list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("DUNGEON:")) {
String set = params[2].substring(8);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true, fileName);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName);
list.add(card);
} else {
@ -833,7 +833,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
destFile = new TFile(CardImageUtils.buildImagePathToTokens() + actualFilename + ".jpg");
}
} else {
destFile = new TFile(CardImageUtils.buildImagePathToCard(card));
destFile = new TFile(CardImageUtils.buildImagePathToCardOrToken(card));
}
// FILE already exists (in zip or in dir)
@ -1008,7 +1008,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
// finished
List<CardDownloadData> downloadedCards = Collections.synchronizedList(new ArrayList<>());
DownloadPicturesService.this.cardsMissing.parallelStream().forEach(cardDownloadData -> {
TFile file = new TFile(CardImageUtils.buildImagePathToCard(cardDownloadData));
TFile file = new TFile(CardImageUtils.buildImagePathToCardOrToken(cardDownloadData));
if (file.exists()) {
downloadedCards.add(cardDownloadData);
}

View file

@ -1,16 +1,13 @@
package org.mage.plugins.card.images;
import com.google.common.base.Function;
import com.google.common.collect.ComputationException;
import mage.abilities.icon.CardIconColor;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.SoftValuesLoadingCache;
import mage.client.util.TransformedImageCache;
import mage.view.CardView;
import net.java.truevfs.access.TFile;
import net.java.truevfs.access.TFileInputStream;
import net.java.truevfs.access.TFileOutputStream;
import org.apache.log4j.Logger;
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
import org.mage.plugins.card.utils.CardImageUtils;
@ -20,7 +17,6 @@ import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -29,165 +25,113 @@ import java.util.regex.Pattern;
* that the images may be garbage collected when they are not needed any more,
* but will be kept as long as possible.
* <p>
* Key format: "[cardname]#[setname]#[type]#[collectorID]#[param]"
* <p>
* where param is:
* <ul>
* <li>size of image</li>
* Key format: "[cardname]#[setname]#[type]#[collectorID]#[image size]#[additional data]"
*
* <li>#Normal: request for unrotated image</li>
* <li>#Tapped: request for rotated image</li>
* <li>#Cropped: request for cropped image that is used for Shandalar like card
* look</li>
* </ul>
*
* @author JayDi85
*/
public final class ImageCache {
private static final Logger LOGGER = Logger.getLogger(ImageCache.class);
private static final SoftValuesLoadingCache<String, BufferedImage> IMAGE_CACHE;
private static final SoftValuesLoadingCache<String, BufferedImage> FACE_IMAGE_CACHE;
private static final SoftValuesLoadingCache<String, BufferedImage> CARD_ICONS_CACHE;
private static final SoftValuesLoadingCache<String, ImageCacheData> IMAGE_CACHE; // cards and tokens
private static final SoftValuesLoadingCache<String, ImageCacheData> FACE_IMAGE_CACHE;
private static final SoftValuesLoadingCache<String, ImageCacheData> CARD_ICONS_CACHE;
/**
* Common pattern for keys. See ImageCache.getKey for structure info
*/
private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)#(.*)");
private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)");
private static final Pattern CARD_ICON_KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)");
static {
// softValues() = Specifies that each value (not key) stored in the map should be wrapped in a SoftReference
// (by default, strong references are used). Softly-referenced objects will be garbage-collected in a
// globally least-recently-used manner, in response to memory demand.
IMAGE_CACHE = SoftValuesLoadingCache.from(new Function<String, BufferedImage>() {
@Override
public BufferedImage apply(String key) {
try {
boolean usesVariousArt = false;
if (key.matches(".*#usesVariousArt.*")) {
usesVariousArt = true;
key = key.replace("#usesVariousArt", "");
IMAGE_CACHE = SoftValuesLoadingCache.from(key -> {
try {
boolean usesVariousArt = false;
if (key.matches(".*#usesVariousArt.*")) {
usesVariousArt = true;
key = key.replace("#usesVariousArt", "");
}
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String setCode = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
}
boolean thumbnail = false;
if (key.matches(".*#thumb.*")) {
thumbnail = true;
key = key.replace("#thumb", "");
}
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String set = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
}
String tokenSetCode = m.group(5);
String tokenDescriptor = m.group(6);
CardDownloadData info = new CardDownloadData(name, setCode, collectorId, usesVariousArt, type);
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
boolean cardback = false;
String path;
if (collectorId.isEmpty() || "0".equals(collectorId)) {
// TOKEN
// can be a normal token or a token from card (see embalm ability)
info.setToken(true);
TFile tokenFile;
boolean cardback = false;
String path;
if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
info.setToken(true);
path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
cardback = true;
// try token image from card
CardDownloadData newInfo = new CardDownloadData(info);
newInfo.setToken(false);
path = CardImageUtils.buildImagePathToCard(newInfo);
TFile tokenFile = getTFile(path);
if (tokenFile == null || !tokenFile.exists()) {
// token empty token image
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
}
} else {
path = CardImageUtils.buildImagePathToCard(info);
// try normal token
path = CardImageUtils.buildImagePathToCardOrToken(info);
tokenFile = getTFile(path);
// try token from card
if (tokenFile == null || !tokenFile.exists()) {
CardDownloadData tempInfo = new CardDownloadData(info);
tempInfo.setToken(false);
path = CardImageUtils.buildImagePathToCardOrToken(info);
tokenFile = getTFile(path);
}
if (path == null) {
return null;
}
TFile file = getTFile(path);
if (file == null) {
return null;
}
if (thumbnail && path.endsWith(".jpg")) {
// need thumbnail image
String thumbnailPath = buildThumbnailPath(path);
TFile thumbnailFile = null;
try {
thumbnailFile = new TFile(thumbnailPath);
} catch (Exception ex) {
}
boolean exists = false;
if (thumbnailFile != null) {
try {
exists = thumbnailFile.exists();
} catch (Exception ex) {
exists = false;
}
}
if (exists) {
LOGGER.debug("loading thumbnail for " + key + ", path=" + thumbnailPath);
BufferedImage thumbnailImage = loadImage(thumbnailFile);
if (thumbnailImage == null) { // thumbnail exists but broken for some reason
LOGGER.warn("failed loading thumbnail for " + key + ", path=" + thumbnailPath
+ ", thumbnail file is probably broken, attempting to recreate it...");
thumbnailImage = makeThumbnailByFile(key, file, thumbnailPath);
}
if (cardback) {
// unknown tokens on opponent desk
thumbnailImage = getRoundCorner(thumbnailImage);
}
return thumbnailImage;
} else {
return makeThumbnailByFile(key, file, thumbnailPath);
}
} else {
if (cardback) {
// need cardback image
BufferedImage image = loadImage(file);
image = getRoundCorner(image);
return image;
} else {
// need normal card image
BufferedImage image = loadImage(file);
image = getWizardsCard(image);
image = getRoundCorner(image);
return image;
}
// try unknown token image
if (tokenFile == null || !tokenFile.exists()) {
// TODO: replace empty token by other default card, not cardback
cardback = true;
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
} else {
throw new RuntimeException(
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key);
// CARD
path = CardImageUtils.buildImagePathToCardOrToken(info);
}
} catch (Exception ex) {
if (ex instanceof ComputationException) {
throw (ComputationException) ex;
} else {
throw new ComputationException(ex);
}
}
}
public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) {
BufferedImage image = loadImage(file);
image = getWizardsCard(image);
image = getRoundCorner(image);
if (image == null) {
return null;
TFile file = getTFile(path);
if (file == null) {
return new ImageCacheData(path, null);
}
if (cardback) {
// TODO: is there any different in images styles? Cardback must be from scryfall, not wizards
// need cardback image
BufferedImage image = loadImage(file);
image = getRoundCorner(image);
return new ImageCacheData(path, image);
} else {
// need normal card image
BufferedImage image = loadImage(file);
image = getWizardsCard(image);
image = getRoundCorner(image);
return new ImageCacheData(path, image);
}
} else {
throw new RuntimeException(
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key);
}
} catch (Exception ex) {
if (ex instanceof ComputationException) {
throw (ComputationException) ex;
} else {
throw new ComputationException(ex);
}
LOGGER.debug("creating thumbnail for " + key);
return makeThumbnail(image, thumbnailPath);
}
});
@ -197,22 +141,18 @@ public final class ImageCache {
if (m.matches()) {
String name = m.group(1);
String set = m.group(2);
//Integer artid = Integer.parseInt(m.group(2));
String setCode = m.group(2);
// skip type
// skip collectorId
String path;
path = CardImageUtils.generateFaceImagePath(name, set);
if (path == null) {
return null;
}
String path = CardImageUtils.generateFaceImagePath(name, setCode);
TFile file = getTFile(path);
if (file == null) {
return null;
return new ImageCacheData(path, null);
}
BufferedImage image = loadImage(file);
return image;
return new ImageCacheData(path, image);
} else {
throw new RuntimeException(
"Requested face image doesn't fit the requirement for key (<cardname>#<artid>#: " + key);
@ -235,7 +175,7 @@ public final class ImageCache {
String resourceName = m.group(2);
CardIconColor cardIconColor = CardIconColor.valueOf(m.group(3));
BufferedImage image = ImageManagerImpl.instance.getCardIcon(resourceName, cardSize, cardIconColor);
return image;
return new ImageCacheData(resourceName, image);
} else {
throw new RuntimeException("Wrong card icons image key format: " + key);
}
@ -255,109 +195,38 @@ public final class ImageCache {
CARD_ICONS_CACHE.invalidateAll();
}
public static String getFilePath(CardView card, int width) {
String key = getKey(card, card.getName(), Integer.toString(width));
boolean usesVariousArt = false;
if (key.matches(".*#usesVariousArt.*")) {
usesVariousArt = true;
key = key.replace("#usesVariousArt", "");
}
boolean thumbnail = false;
if (key.matches(".*#thumb.*")) {
thumbnail = true;
key = key.replace("#thumb", "");
}
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String set = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
}
String tokenSetCode = m.group(5);
String tokenDescriptor = m.group(6);
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
String path;
if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
info.setToken(true);
path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
// try token image from card
CardDownloadData newInfo = new CardDownloadData(info);
newInfo.setToken(false);
path = CardImageUtils.buildImagePathToCard(newInfo);
TFile tokenFile = getTFile(path);
if (tokenFile == null || !tokenFile.exists()) {
// token empty token image
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
}
} else {
path = CardImageUtils.buildImagePathToCard(info);
}
if (thumbnail && path.endsWith(".jpg")) {
return buildThumbnailPath(path);
}
return path;
}
return "";
}
private ImageCache() {
}
public static BufferedImage getCardbackImage() {
BufferedImage image = ImageCache.loadImage(new TFile(CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename)));
public static ImageCacheData getCardbackImage() {
String path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
BufferedImage image = ImageCache.loadImage(getTFile(path));
image = getRoundCorner(image);
return image;
return new ImageCacheData(path, image);
}
public static BufferedImage getMorphImage() {
// TODO: replace by morth image
CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0, "KTK", "");
public static ImageCacheData getMorphImage() {
// TODO: replace by downloadable morth image
CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0);
info.setToken(true);
String path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
return null;
}
TFile file = getTFile(path);
String path = CardImageUtils.buildImagePathToCardOrToken(info);
TFile file = getTFile(path);
BufferedImage image = loadImage(file);
image = getRoundCorner(image);
return image;
return new ImageCacheData(path, image);
}
public static BufferedImage getManifestImage() {
// TODO: replace by manifestest image
CardDownloadData info = new CardDownloadData("Manifest", "FRF", "0", false, 0, "FRF", "");
public static ImageCacheData getManifestImage() {
// TODO: replace by downloadable manifestest image
CardDownloadData info = new CardDownloadData("Manifest", "FRF", "0", false, 0);
info.setToken(true);
String path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
return null;
}
TFile file = getTFile(path);
String path = CardImageUtils.buildImagePathToCardOrToken(info);
TFile file = getTFile(path);
BufferedImage image = loadImage(file);
image = getRoundCorner(image);
return image;
}
private static String buildThumbnailPath(String path) {
String thumbnailPath;
if (PreferencesDialog.isSaveImagesToZip()) {
thumbnailPath = path.replace(".zip", ".thumb.zip");
} else {
thumbnailPath = path.replace(".jpg", ".thumb.jpg");
}
return thumbnailPath;
return new ImageCacheData(path, image);
}
public static BufferedImage getRoundCorner(BufferedImage image) {
@ -387,6 +256,7 @@ public final class ImageCache {
}
public static BufferedImage getWizardsCard(BufferedImage image) {
// TODO: can be removed?
if (image != null && image.getWidth() == 265 && image.getHeight() == 370) {
BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = crop.createGraphics();
@ -398,80 +268,61 @@ public final class ImageCache {
}
}
public static boolean isFaceImagePresent(CardView card) {
String path;
path = CardImageUtils.generateFaceImagePath(card.getName(), card.getExpansionSetCode());
if (path == null) {
return false;
}
TFile file = getTFile(path);
if (file == null) {
return false;
}
return file.exists();
public static ImageCacheData getImageOriginal(CardView card) {
return getImage(getKey(card, card.getName(), 0));
}
public static BufferedImage getThumbnail(CardView card) {
return getImage(getKey(card, card.getName(), "#thumb"));
public static ImageCacheData getImageOriginalAlternateName(CardView card) {
return getImage(getKey(card, card.getAlternateName(), 0));
}
public static BufferedImage tryGetThumbnail(CardView card) {
return tryGetImage(getKey(card, card.getName(), "#thumb"));
}
public static BufferedImage getImageOriginal(CardView card) {
return getImage(getKey(card, card.getName(), ""));
}
public static BufferedImage getImageOriginalAlternateName(CardView card) {
return getImage(getKey(card, card.getAlternateName(), ""));
}
public static BufferedImage getCardIconImage(String resourceName, int iconSize, String cardColorName) {
public static ImageCacheData getCardIconImage(String resourceName, int iconSize, String cardColorName) {
return getCardIconImage(getCardIconKey(resourceName, iconSize, cardColorName));
}
/**
* Returns the Image corresponding to the key
*/
private static BufferedImage getImage(String key) {
private static ImageCacheData getImage(String key) {
try {
return IMAGE_CACHE.getOrNull(key);
ImageCacheData data = IMAGE_CACHE.getOrNull(key);
return data != null ? data : new ImageCacheData("ERROR: key - " + key, null);
} catch (ComputationException ex) {
// too low memory
if (ex.getCause() instanceof NullPointerException) {
return null;
return new ImageCacheData("ERROR: low memory?", null);
}
LOGGER.error(ex, ex);
return null;
return new ImageCacheData("ERROR: see logs", null);
}
}
/**
* Returns the Image corresponding to the key
*/
private static BufferedImage getFaceImage(String key) {
private static ImageCacheData getFaceImage(String key) {
try {
return FACE_IMAGE_CACHE.getOrNull(key);
ImageCacheData data = FACE_IMAGE_CACHE.getOrNull(key);
return data != null ? data : new ImageCacheData("ERROR: key " + key, null);
} catch (ComputationException ex) {
if (ex.getCause() instanceof NullPointerException) {
return null;
return new ImageCacheData("ERROR: low memory?", null);
}
LOGGER.error(ex, ex);
return null;
return new ImageCacheData("ERROR: see logs", null);
}
}
private static BufferedImage getCardIconImage(String key) {
private static ImageCacheData getCardIconImage(String key) {
try {
return CARD_ICONS_CACHE.getOrNull(key);
ImageCacheData data = CARD_ICONS_CACHE.getOrNull(key);
return data != null ? data : new ImageCacheData("ERROR: key - " + key, null);
} catch (ComputationException ex) {
if (ex.getCause() instanceof NullPointerException) {
return null;
return new ImageCacheData("ERROR: low memory?", null);
}
LOGGER.error(ex, ex);
return null;
return new ImageCacheData("ERROR: see logs", null);
}
}
@ -479,22 +330,24 @@ public final class ImageCache {
* Returns the Image corresponding to the key only if it already exists in
* the cache.
*/
private static BufferedImage tryGetImage(String key) {
private static ImageCacheData tryGetImage(String key) {
return IMAGE_CACHE.peekIfPresent(key);
}
/**
* Returns the map key for a card, without any suffixes for the image size.
* Generate key for images cache (it must contain all info to search and load image from the disk)
*
* @param card
* @param cardName - can be alternative name
* @param imageSize - size info, 0 to use original image (with max size)
*/
private static String getKey(CardView card, String name, String suffix) {
return (card.isToken() ? name.replace(" Token", "") : name)
private static String getKey(CardView card, String cardName, int imageSize) {
return (card.isToken() ? cardName.replace(" Token", "") : cardName)
+ '#' + card.getExpansionSetCode()
+ '#' + card.getType()
+ '#' + card.getCardNumber()
+ '#' + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode())
+ suffix
+ (card.getUsesVariousArt() ? "#usesVariousArt" : "")
+ '#' + (card.getTokenDescriptor() != null ? card.getTokenDescriptor() : "");
+ '#' + imageSize
+ (card.getUsesVariousArt() ? "#usesVariousArt" : "");
}
/**
@ -534,24 +387,6 @@ public final class ImageCache {
return image;
}
public static BufferedImage makeThumbnail(BufferedImage original, String path) {
BufferedImage image = TransformedImageCache.getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL.width, Constants.THUMBNAIL_SIZE_FULL.height);
TFile imageFile = getTFile(path);
if (imageFile == null) {
return null;
}
try {
try (TFileOutputStream outputStream = new TFileOutputStream(imageFile)) {
String format = image.getColorModel().getNumComponents() > 3 ? "png" : "jpg";
ImageIO.write(image, format, outputStream);
}
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
imageFile.delete();
}
return image;
}
/**
* Returns an image scaled to the size given
*
@ -584,23 +419,22 @@ public final class ImageCache {
* @param height
* @return
*/
public static BufferedImage getImage(CardView card, int width, int height) {
if (Constants.THUMBNAIL_SIZE_FULL.width + 10 > width) {
return getThumbnail(card);
}
String key = getKey(card, card.getName(), Integer.toString(width));
BufferedImage original = getImage(key);
if (original == null) {
LOGGER.debug(key + " not found");
return null;
public static ImageCacheData getImage(CardView card, int width, int height) {
String key = getKey(card, card.getName(), width);
ImageCacheData data = getImage(key);
if (data.getImage() == null) {
LOGGER.debug("Image doesn't exists in the cache: " + key);
return data;
}
double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight());
double scale = Math.min((double) width / data.getImage().getWidth(), (double) height / data.getImage().getHeight());
if (scale >= 1) {
return original;
return data;
}
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
BufferedImage newImage = TransformedImageCache.getResizedImage(data.getImage(), (int) (data.getImage().getWidth() * scale), (int) (data.getImage().getHeight() * scale));
data.setImage(newImage);
return data;
}
/**
@ -611,15 +445,13 @@ public final class ImageCache {
* @param height
* @return
*/
public static BufferedImage getFaceImage(CardView card, int width, int height) {
public static ImageCacheData getFaceImage(CardView card, int width, int height) {
String key = getFaceKey(card, card.getName(), card.getExpansionSetCode());
BufferedImage original = getFaceImage(key);
if (original == null) {
ImageCacheData data = getFaceImage(key);
if (data.getImage() == null) {
LOGGER.debug(key + " (faceimage) not found");
return null;
}
return original;
return data;
}
/**
@ -632,29 +464,31 @@ public final class ImageCache {
* @param height
* @return
*/
public static BufferedImage tryGetImage(CardView card, int width, int height) {
if (Constants.THUMBNAIL_SIZE_FULL.width + 10 > width) {
return tryGetThumbnail(card);
}
String key = getKey(card, card.getName(), Integer.toString(width));
BufferedImage original = tryGetImage(key);
if (original == null) {
public static ImageCacheData tryGetImage(CardView card, int width, int height) {
String key = getKey(card, card.getName(), width);
ImageCacheData data = tryGetImage(key);
if (data.getImage() == null) {
LOGGER.debug(key + " not found");
return null;
return data;
}
double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight());
double scale = Math.min((double) width / data.getImage().getWidth(), (double) height / data.getImage().getHeight());
if (scale >= 1) {
return original;
return data;
}
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
BufferedImage newImage = TransformedImageCache.getResizedImage(data.getImage(), (int) (data.getImage().getWidth() * scale), (int) (data.getImage().getHeight() * scale));
data.setImage(newImage);
return data;
}
public static TFile getTFile(String path) {
try {
return new TFile(path);
if (path != null) {
return new TFile(path);
}
} catch (NullPointerException ex) {
// TODO: raise error on path == null -- is it actual?!
LOGGER.warn("Imagefile does not exist: " + path);
}
return null;

View file

@ -0,0 +1,28 @@
package org.mage.plugins.card.images;
import java.awt.image.BufferedImage;
/**
* @author JayDi85
*/
public class ImageCacheData {
String path;
BufferedImage image;
public ImageCacheData(String path, BufferedImage image) {
this.path = path;
this.image = image;
}
public String getPath() {
return path;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}

View file

@ -5,7 +5,6 @@ import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection;
import mage.remote.Connection.ProxyType;
import net.java.truevfs.access.TFile;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@ -26,87 +25,13 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.prefs.Preferences;
public final class CardImageUtils {
private static final HashMap<CardDownloadData, String> pathCache = new HashMap<>();
private static final Logger LOGGER = Logger.getLogger(CardImageUtils.class);
/**
* @param card
* @return String if image exists, else null
*/
public static String generateTokenImagePath(CardDownloadData card) {
if (card.isToken()) {
String filePath = getTokenImagePath(card);
if (pathCache.containsKey(card)) {
if (filePath.equals(pathCache.get(card))) {
return pathCache.get(card);
}
}
TFile file = new TFile(filePath);
if (!file.exists() && card.getTokenSetCode() != null) {
filePath = searchForCardImage(card);
file = new TFile(filePath);
}
if (file.exists()) {
pathCache.put(card, filePath);
return filePath;
}
//log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + getTokenImagePath(card));
} else {
LOGGER.warn("Trying to get token path for non token card. Set: " + card.getSet() + " Set Code: " + card.getTokenSetCode() + " Name: " + card.getName());
}
return null;
}
/**
* @param card
* @return String regardless of whether image exists
*/
public static String generateFullTokenImagePath(CardDownloadData card) {
if (card.isToken()) {
return getTokenImagePath(card);
}
return "";
}
private static String getTokenImagePath(CardDownloadData card) {
String filename = buildImagePathToCard(card);
TFile file = new TFile(filename);
if (!file.exists()) {
String tokenDescriptorfilename = generateTokenDescriptorImagePath(card);
if (!tokenDescriptorfilename.isEmpty()) {
file = new TFile(filename);
if (file.exists()) {
return tokenDescriptorfilename;
}
}
}
return filename;
}
private static String searchForCardImage(CardDownloadData card) {
TFile file;
String path;
CardDownloadData c = new CardDownloadData(card);
c.setSet(card.getTokenSetCode());
path = getTokenImagePath(c);
file = new TFile(path);
if (file.exists()) {
pathCache.put(card, path);
return path;
}
return generateTokenDescriptorImagePath(card);
}
public static String prepareCardNameForFile(String cardName) {
return cardName
.replace(":", "")
@ -141,12 +66,12 @@ public final class CardImageUtils {
return getImagesDir() + Constants.RESOURCE_PATH_DEFAULT_IMAGES + File.separator + defaultFileName;
}
public static String fixSetNameForWindows(String set) {
public static String fixSetNameForWindows(String setCode) {
// windows can't create con folders
if (set.equals("CON") || set.equals("con")) {
if (setCode.equals("CON") || setCode.equals("con")) {
return "COX";
} else {
return set;
return setCode;
}
}
@ -160,40 +85,34 @@ public final class CardImageUtils {
}
}
private static String buildImagePathToTokenDescriptor(CardDownloadData card) {
return buildImagePathToTokens() + card.getTokenDescriptor() + ".full.jpg";
}
public static String buildImagePathToSet(CardDownloadData card) {
if (card.getSet() == null) {
throw new IllegalArgumentException("Card " + card.getName() + " have empty set.");
}
String set = card.getSet().toUpperCase(Locale.ENGLISH);
String setCode = card.getSet().toUpperCase(Locale.ENGLISH);
if (card.isToken()) {
return buildImagePathToSetAsToken(set);
return buildImagePathToSetAsToken(setCode);
} else {
return buildImagePathToSetAsCard(set);
return buildImagePathToSetAsCard(setCode);
}
}
private static String buildImagePathToSetAsCard(String set) {
private static String buildImagePathToSetAsCard(String setCode) {
String imagesPath = getImagesDir() + File.separator;
if (PreferencesDialog.isSaveImagesToZip()) {
return imagesPath + fixSetNameForWindows(set) + ".zip" + File.separator + fixSetNameForWindows(set) + File.separator;
return imagesPath + fixSetNameForWindows(setCode) + ".zip" + File.separator + fixSetNameForWindows(setCode) + File.separator;
} else {
return imagesPath + fixSetNameForWindows(set) + File.separator;
return imagesPath + fixSetNameForWindows(setCode) + File.separator;
}
}
private static String buildImagePathToSetAsToken(String set) {
return buildImagePathToTokens() + fixSetNameForWindows(set) + File.separator;
private static String buildImagePathToSetAsToken(String setCode) {
return buildImagePathToTokens() + fixSetNameForWindows(setCode) + File.separator;
}
public static String buildImagePathToCard(CardDownloadData card) {
public static String buildImagePathToCardOrToken(CardDownloadData card) {
String setPath = buildImagePathToSet(card);
@ -207,7 +126,7 @@ public final class CardImageUtils {
cardName = prepareCardNameForFile(card.getName());
}
String finalFileName = "";
String finalFileName;
if (card.getUsesVariousArt()) {
// different arts uses name + collector id
finalFileName = cardName + prefixType + '.' + card.getCollectorIdAsFileName() + ".full.jpg";
@ -216,54 +135,11 @@ public final class CardImageUtils {
finalFileName = cardName + prefixType + ".full.jpg";
}
/* 2019-01-12: no needs in name corrections, all files must be same and auto-downloaded
// if image file exists, correct name (for case sensitive systems)
// use TFile for zips
TFile dirFile = new TFile(setPath);
TFile imageFile = new TFile(setPath + finalFileName);
// warning, zip files can be broken
try {
if (dirFile.exists() && !imageFile.exists()) {
// search like names
for (String fileName : dirFile.list()) {
if (fileName.toLowerCase(Locale.ENGLISH).equals(finalFileName.toLowerCase(Locale.ENGLISH))) {
finalFileName = fileName;
break;
}
}
}
} catch (Exception ex) {
log.error("Can't read card name from file, may be it broken: " + setPath);
}
*/
return setPath + finalFileName;
}
public static String generateFaceImagePath(String cardname, String set) {
return getImagesDir() + File.separator + "FACE" + File.separator + fixSetNameForWindows(set) + File.separator + prepareCardNameForFile(cardname) + ".jpg";
}
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
String straightImageFile = buildImagePathToTokenDescriptor(card);
TFile file = new TFile(straightImageFile);
if (file.exists()) {
return straightImageFile;
}
straightImageFile = straightImageFile.replaceFirst("\\.[0-9]+\\.[0-9]+", ".X.X");
file = new TFile(straightImageFile);
if (file.exists()) {
return straightImageFile;
}
straightImageFile = straightImageFile.replaceFirst("\\.X\\.X", ".S.S");
file = new TFile(straightImageFile);
if (file.exists()) {
return straightImageFile;
}
return "";
public static String generateFaceImagePath(String cardName, String setCode) {
return getImagesDir() + File.separator + "FACE" + File.separator + fixSetNameForWindows(setCode) + File.separator + prepareCardNameForFile(cardName) + ".jpg";
}
public static Proxy getProxyFromPreferences() {
@ -305,8 +181,7 @@ public final class CardImageUtils {
}
public static void checkAndFixImageFiles() {
// search broken files and delete it (zero size files)
// search temp files and delete it (.tmp files from zip library)
// search broken, temp or outdated files and delete it
Path rootPath = new File(CardImageUtils.getImagesDir()).toPath();
if (!Files.exists(rootPath)) {
return;
@ -314,6 +189,7 @@ public final class CardImageUtils {
Collection<Path> brokenFilesList = new ArrayList<>();
Collection<Path> tempFilesList = new ArrayList<>();
Collection<Path> outdatedFilesList = new ArrayList<>();
try {
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override
@ -329,6 +205,11 @@ public final class CardImageUtils {
tempFilesList.add(file);
}
// 3. outdated files delete without warning
if (file.toString().endsWith(".thumb.zip")) {
outdatedFilesList.add(file);
}
return FileVisitResult.CONTINUE;
}
});
@ -336,10 +217,13 @@ public final class CardImageUtils {
LOGGER.error("Can't load files list from images folder: " + rootPath.toAbsolutePath().toString(), e);
}
// temp files must be deleted without errors
for (Path tempFile : tempFilesList) {
// temp and outdated files must be deleted without errors
Collection<Path> list = new ArrayList<>();
list.addAll(tempFilesList);
list.addAll(outdatedFilesList);
for (Path path : list) {
try {
Files.delete(tempFile);
Files.delete(path);
} catch (Throwable e) {
// ignore any error (e.g. it opened by xmage app)
}
@ -353,7 +237,7 @@ public final class CardImageUtils {
Files.delete(brokenFile);
} catch (Throwable e) {
// stop clean on any error
LOGGER.error("Images check: ERROR, can't delete broken file: " + brokenFile.toAbsolutePath().toString(), e);
LOGGER.error("Images check: ERROR, can't delete broken file: " + brokenFile.toAbsolutePath(), e);
break;
}
}

View file

@ -18,16 +18,16 @@ public class TokensMtgImageSourceTest {
public void generateTokenUrlTest() throws Exception {
CardImageSource imageSource = TokensMtgImageSource.instance;
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1, "ORI", ""));
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1));
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2, "ORI", ""));
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2));
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0, "ORI", ""));
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0));
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0, null, ""));
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0));
Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
}
}

View file

@ -273,7 +273,7 @@ public class CardView extends SimpleCardView {
* @param storeZone if true the card zone will be set in the zone attribute.
*/
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor());
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), game != null);
this.originalCard = card;
// no information available for face down cards as long it's not a controlled face down morph card
@ -487,7 +487,6 @@ public class CardView extends SimpleCardView {
} else {
// a created token
this.expansionSetCode = card.getExpansionSetCode();
this.tokenDescriptor = card.getTokenDescriptor();
}
//
// set code and card number for token copies to get the image
@ -600,7 +599,7 @@ public class CardView extends SimpleCardView {
}
public CardView(MageObject object, Game game) {
super(object.getId(), "", "0", false, "", true, "");
super(object.getId(), "", "0", false, true);
this.originalCard = null;
this.name = object.getName();
@ -674,7 +673,7 @@ public class CardView extends SimpleCardView {
}
protected CardView() {
super(null, "", "0", false, "", true, "");
super(null, "", "0", false, true);
}
public CardView(EmblemView emblem) {
@ -739,7 +738,7 @@ public class CardView extends SimpleCardView {
}
public CardView(boolean empty) {
super(null, "", "0", false, "", "");
super(null, "", "0", false);
if (!empty) {
throw new IllegalArgumentException("Not supported.");
}
@ -793,9 +792,10 @@ public class CardView extends SimpleCardView {
}
CardView(Token token, Game game) {
super(token.getId(), "", "0", false, "", "");
super(token.getId(), "", "0", false);
this.isToken = true;
this.id = token.getId();
this.expansionSetCode = token.getOriginalExpansionSetCode();
this.name = token.getName();
this.displayName = token.getName();
this.displayFullName = token.getName();
@ -814,8 +814,6 @@ public class CardView extends SimpleCardView {
this.manaCostRightStr = "";
this.rarity = Rarity.SPECIAL;
this.type = token.getTokenType();
this.tokenDescriptor = token.getTokenDescriptor();
this.tokenSetCode = token.getOriginalExpansionSetCode();
}
protected final void addTargets(Targets targets, Effects effects, Ability source, Game game) {

View file

@ -20,7 +20,7 @@ public class LookedAtView implements Serializable {
public LookedAtView(String name, Cards cards, Game game) {
this.name = name;
for (Card card: cards.getCards(game)) {
this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), card.getTokenDescriptor()));
this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt()));
}
}

View file

@ -54,8 +54,7 @@ public class PermanentView extends CardView {
if (isToken()) {
original = new CardView(((PermanentToken) permanent).getToken().copy(), (Game) null);
original.expansionSetCode = permanent.getExpansionSetCode();
tokenSetCode = original.getTokenSetCode();
tokenDescriptor = original.getTokenDescriptor();
expansionSetCode = permanent.getExpansionSetCode();
} else {
if (card != null) {
// original may not be face down

View file

@ -13,8 +13,6 @@ public class SimpleCardView implements Serializable, SelectableObjectView {
@Expose
protected UUID id;
protected String expansionSetCode;
protected String tokenSetCode;
protected String tokenDescriptor;
protected String cardNumber;
protected boolean usesVariousArt;
protected boolean gameObject;
@ -26,28 +24,23 @@ public class SimpleCardView implements Serializable, SelectableObjectView {
public SimpleCardView(final SimpleCardView view) {
this.id = view.id;
this.expansionSetCode = view.expansionSetCode;
this.tokenSetCode = view.tokenSetCode;
this.tokenDescriptor = view.tokenDescriptor;
this.cardNumber = view.cardNumber;
this.usesVariousArt = view.usesVariousArt;
this.gameObject = view.gameObject;
this.isChoosable = view.isChoosable;
this.isSelected = view.isSelected;
this.playableStats = view.playableStats.copy();
}
public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, String tokenSetCode, String tokenDescriptor) {
this(id, expansionSetCode, cardNumber, usesVariousArt, tokenSetCode, false, tokenDescriptor);
public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt) {
this(id, expansionSetCode, cardNumber, usesVariousArt, false);
}
public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, String tokenSetCode, boolean isGameObject, String tokenDescriptor) {
public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, boolean isGameObject) {
this.id = id;
this.expansionSetCode = expansionSetCode;
this.tokenDescriptor = tokenDescriptor;
this.cardNumber = cardNumber;
this.usesVariousArt = usesVariousArt;
this.tokenSetCode = tokenSetCode;
this.gameObject = isGameObject;
}
@ -67,14 +60,6 @@ public class SimpleCardView implements Serializable, SelectableObjectView {
return usesVariousArt;
}
public String getTokenSetCode() {
return tokenSetCode;
}
public String getTokenDescriptor() {
return tokenDescriptor;
}
public boolean isGameObject() {
return gameObject;
}

View file

@ -18,8 +18,7 @@ public class SimpleCardsView extends LinkedHashMap<UUID, SimpleCardView> {
public SimpleCardsView(Collection<Card> cards, boolean isGameObject) {
for (Card card: cards) {
this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), isGameObject,
card.getTokenDescriptor()));
this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), isGameObject));
}
}

View file

@ -0,0 +1,35 @@
package org.mage.test.serverside;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.PermanentToken;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class TokenImagesTest extends CardTestPlayerBase {
@Test
public void test_TokenMustGetSameSetCodeAsSourceCard() {
//{3}{W}, {T}, Sacrifice Memorial to Glory: Create two 1/1 white Soldier creature tokens.
addCard(Zone.BATTLEFIELD, playerA, "40K:Memorial to Glory");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{W}, {T}, Sacrifice");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Soldier Token", 2);
currentGame.getBattlefield().getAllPermanents().stream()
.filter(card -> card.getName().equals("Soldier Token"))
.forEach(card -> {
Assert.assertEquals("40K", card.getExpansionSetCode());
Assert.assertEquals("40K", ((PermanentToken) card).getToken().getOriginalExpansionSetCode());
});
}
}

View file

@ -622,7 +622,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
Assert.fail("Can't add card " + cardName + " - alias " + aliasName + " already exists for " + player.getName());
}
// set code for
// set code for card
String setCode = "";
String setLookup = CardUtil.substring(cardName, CardUtil.TESTS_SET_CODE_LOOKUP_LENGTH);
if (setLookup.contains(":")) {

View file

@ -49,11 +49,6 @@ public interface Card extends MageObject {
List<String> getRules(Game game); // gets card rules + in game modifications
String getExpansionSetCode();
String getTokenSetCode();
String getTokenDescriptor();
void checkForCountersToAdd(Permanent permanent, Ability source, Game game);
void setFaceDown(boolean value, Game game);

View file

@ -42,8 +42,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
protected UUID ownerId;
protected String cardNumber;
protected String expansionSetCode;
protected String tokenSetCode;
protected String tokenDescriptor;
protected Rarity rarity;
protected Class<? extends Card> secondSideCardClazz;
protected Class<? extends Card> meldsWithClazz;
@ -121,8 +119,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
ownerId = card.ownerId;
cardNumber = card.cardNumber;
expansionSetCode = card.expansionSetCode;
tokenSetCode = card.tokenSetCode;
tokenDescriptor = card.tokenDescriptor;
rarity = card.rarity;
secondSideCardClazz = card.secondSideCardClazz;
@ -378,16 +374,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
return expansionSetCode;
}
@Override
public String getTokenSetCode() {
return tokenSetCode;
}
@Override
public String getTokenDescriptor() {
return tokenDescriptor;
}
@Override
public List<Mana> getMana() {
List<Mana> mana = new ArrayList<>();

View file

@ -87,7 +87,6 @@ public class PermanentToken extends PermanentImpl {
this.supertype.clear();
this.supertype.addAll(token.getSuperType());
this.subtype.copyFrom(token.getSubtype(game));
this.tokenDescriptor = token.getTokenDescriptor();
this.startingLoyalty = token.getStartingLoyalty();
// workaround for entersTheBattlefield replacement effects
if (this.abilities.containsClass(ChangelingAbility.class)) {

View file

@ -17,8 +17,6 @@ public interface Token extends MageObject {
@Override
Token copy();
String getTokenDescriptor();
String getDescription();
List<UUID> getLastAddedTokenIds();

View file

@ -85,12 +85,6 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
this.tokenDescriptor = tokenDescriptor();
}
@Override
public String getTokenDescriptor() {
this.tokenDescriptor = tokenDescriptor();
return tokenDescriptor;
}
private String tokenDescriptor() {
String strName = this.name.replaceAll("[^a-zA-Z0-9]", "");
String strColor = this.color.toString().replaceAll("[^a-zA-Z0-9]", "");

View file

@ -697,16 +697,6 @@ public class Spell extends StackObjectImpl implements Card {
return card.getExpansionSetCode();
}
@Override
public String getTokenSetCode() {
return card.getTokenSetCode();
}
@Override
public String getTokenDescriptor() {
return card.getTokenDescriptor();
}
@Override
public void setFaceDown(boolean value, Game game) {
faceDown = value;