Added support for Unicode (non-English) metadata.
- Setting is off by default, and can be switched on in the options menu. - Changed default font to "Arial Unicode MS" (CJK), with fallback to "Lucida Sans Console" (non-CJK). Cross-platform support may be added later. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
9a94c03b4e
commit
904a54df26
|
@ -177,7 +177,7 @@ public class MusicController {
|
||||||
public static String getTrackName() {
|
public static String getTrackName() {
|
||||||
if (!trackExists() || lastOsu == null)
|
if (!trackExists() || lastOsu == null)
|
||||||
return null;
|
return null;
|
||||||
return lastOsu.title;
|
return lastOsu.getTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,7 +186,7 @@ public class MusicController {
|
||||||
public static String getArtistName() {
|
public static String getArtistName() {
|
||||||
if (!trackExists() || lastOsu == null)
|
if (!trackExists() || lastOsu == null)
|
||||||
return null;
|
return null;
|
||||||
return lastOsu.artist;
|
return lastOsu.getArtist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu;
|
package itdelatrisu.opsu;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.states.Options;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@ -108,9 +110,28 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the associated file object.
|
* Returns the associated file object.
|
||||||
|
* @return the File object
|
||||||
*/
|
*/
|
||||||
public File getFile() { return file; }
|
public File getFile() { return file; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the song title.
|
||||||
|
* If configured, the Unicode string will be returned instead.
|
||||||
|
* @return the song title
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return (Options.useUnicodeMetadata() && !titleUnicode.isEmpty()) ? titleUnicode : title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the song artist.
|
||||||
|
* If configured, the Unicode string will be returned instead.
|
||||||
|
* @return the song artist
|
||||||
|
*/
|
||||||
|
public String getArtist() {
|
||||||
|
return (Options.useUnicodeMetadata() && !artistUnicode.isEmpty()) ? artistUnicode : artist;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the background associated with the OsuFile.
|
* Draws the background associated with the OsuFile.
|
||||||
* @param width the container width
|
* @param width the container width
|
||||||
|
@ -154,6 +175,6 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s - %s [%s]", artist, title, version);
|
return String.format("%s - %s [%s]", getArtist(), getTitle(), version);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -95,9 +95,9 @@ public class OsuGroupNode {
|
||||||
float cx = x + (bg.getWidth() * 0.05f) - xOffset;
|
float cx = x + (bg.getWidth() * 0.05f) - xOffset;
|
||||||
float cy = y + (bg.getHeight() * 0.2f) - 3;
|
float cy = y + (bg.getHeight() * 0.2f) - 3;
|
||||||
|
|
||||||
Utils.FONT_MEDIUM.drawString(cx, cy, osu.title, textColor);
|
Utils.FONT_MEDIUM.drawString(cx, cy, osu.getTitle(), textColor);
|
||||||
Utils.FONT_DEFAULT.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() - 4,
|
Utils.FONT_DEFAULT.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() - 4,
|
||||||
String.format("%s // %s", osu.artist, osu.creator), textColor);
|
String.format("%s // %s", osu.getArtist(), osu.creator), textColor);
|
||||||
if (expanded || osuFiles.size() == 1)
|
if (expanded || osuFiles.size() == 1)
|
||||||
Utils.FONT_BOLD.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() - 8,
|
Utils.FONT_BOLD.drawString(cx, cy + Utils.FONT_MEDIUM.getLineHeight() + Utils.FONT_DEFAULT.getLineHeight() - 8,
|
||||||
osu.version, textColor);
|
osu.version, textColor);
|
||||||
|
@ -144,7 +144,7 @@ public class OsuGroupNode {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (osuFileIndex == -1)
|
if (osuFileIndex == -1)
|
||||||
return String.format("%s - %s", osuFiles.get(0).artist, osuFiles.get(0).title);
|
return String.format("%s - %s", osuFiles.get(0).getArtist(), osuFiles.get(0).getTitle());
|
||||||
else
|
else
|
||||||
return osuFiles.get(osuFileIndex).toString();
|
return osuFiles.get(osuFileIndex).toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,11 @@ package itdelatrisu.opsu;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -110,7 +112,7 @@ public class OsuParser {
|
||||||
private static OsuFile parseFile(File file, ArrayList<OsuFile> osuFiles, boolean parseObjects) {
|
private static OsuFile parseFile(File file, ArrayList<OsuFile> osuFiles, boolean parseObjects) {
|
||||||
OsuFile osu = new OsuFile(file);
|
OsuFile osu = new OsuFile(file);
|
||||||
|
|
||||||
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
|
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
|
||||||
|
|
||||||
// initialize timing point list
|
// initialize timing point list
|
||||||
osu.timingPoints = new ArrayList<OsuTimingPoint>();
|
osu.timingPoints = new ArrayList<OsuTimingPoint>();
|
||||||
|
|
|
@ -21,9 +21,12 @@ package itdelatrisu.opsu;
|
||||||
import itdelatrisu.opsu.states.Options;
|
import itdelatrisu.opsu.states.Options;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
@ -104,6 +107,11 @@ public class Utils {
|
||||||
cursorX = new LinkedList<Integer>(),
|
cursorX = new LinkedList<Integer>(),
|
||||||
cursorY = new LinkedList<Integer>();
|
cursorY = new LinkedList<Integer>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of all Unicode strings already loaded.
|
||||||
|
*/
|
||||||
|
private static HashSet<String> loadedGlyphs = new HashSet<String>();
|
||||||
|
|
||||||
// game-related variables
|
// game-related variables
|
||||||
private static GameContainer container;
|
private static GameContainer container;
|
||||||
private static StateBasedGame game;
|
private static StateBasedGame game;
|
||||||
|
@ -148,15 +156,23 @@ public class Utils {
|
||||||
// create fonts
|
// create fonts
|
||||||
float fontBase;
|
float fontBase;
|
||||||
if (height <= 600)
|
if (height <= 600)
|
||||||
fontBase = 9f;
|
|
||||||
else if (height < 800)
|
|
||||||
fontBase = 10f;
|
fontBase = 10f;
|
||||||
|
else if (height < 800)
|
||||||
|
fontBase = 11f;
|
||||||
else if (height <= 900)
|
else if (height <= 900)
|
||||||
fontBase = 12f;
|
fontBase = 13f;
|
||||||
else
|
else
|
||||||
fontBase = 14f;
|
fontBase = 15f;
|
||||||
|
|
||||||
Font font = new Font("Lucida Sans Unicode", Font.PLAIN, (int) (fontBase * 4 / 3));
|
// TODO: cross-platform multilingual support
|
||||||
|
String fontName = "";
|
||||||
|
String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
||||||
|
if (Arrays.asList(fontNames).contains("Arial Unicode MS"))
|
||||||
|
fontName = "Arial Unicode MS";
|
||||||
|
else
|
||||||
|
fontName = "Lucida Sans Console";
|
||||||
|
|
||||||
|
Font font = new Font(fontName, Font.PLAIN, (int) (fontBase * 4 / 3));
|
||||||
FONT_DEFAULT = new UnicodeFont(font);
|
FONT_DEFAULT = new UnicodeFont(font);
|
||||||
FONT_BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));
|
FONT_BOLD = new UnicodeFont(font.deriveFont(Font.BOLD));
|
||||||
FONT_XLARGE = new UnicodeFont(font.deriveFont(fontBase * 4));
|
FONT_XLARGE = new UnicodeFont(font.deriveFont(fontBase * 4));
|
||||||
|
@ -485,4 +501,35 @@ public class Utils {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds and loads glyphs for an OsuFile's Unicode title and artist strings.
|
||||||
|
* @param osu the OsuFile
|
||||||
|
*/
|
||||||
|
public static void loadGlyphs(OsuFile osu) {
|
||||||
|
boolean glyphsAdded = false;
|
||||||
|
if (!osu.titleUnicode.isEmpty() && !loadedGlyphs.contains(osu.titleUnicode)) {
|
||||||
|
Utils.FONT_LARGE.addGlyphs(osu.titleUnicode);
|
||||||
|
Utils.FONT_MEDIUM.addGlyphs(osu.titleUnicode);
|
||||||
|
Utils.FONT_DEFAULT.addGlyphs(osu.titleUnicode);
|
||||||
|
loadedGlyphs.add(osu.titleUnicode);
|
||||||
|
glyphsAdded = true;
|
||||||
|
}
|
||||||
|
if (!osu.artistUnicode.isEmpty() && !loadedGlyphs.contains(osu.artistUnicode)) {
|
||||||
|
Utils.FONT_LARGE.addGlyphs(osu.artistUnicode);
|
||||||
|
Utils.FONT_MEDIUM.addGlyphs(osu.artistUnicode);
|
||||||
|
Utils.FONT_DEFAULT.addGlyphs(osu.artistUnicode);
|
||||||
|
loadedGlyphs.add(osu.artistUnicode);
|
||||||
|
glyphsAdded = true;
|
||||||
|
}
|
||||||
|
if (glyphsAdded && Options.useUnicodeMetadata()) {
|
||||||
|
try {
|
||||||
|
Utils.FONT_LARGE.loadGlyphs();
|
||||||
|
Utils.FONT_MEDIUM.loadGlyphs();
|
||||||
|
Utils.FONT_DEFAULT.loadGlyphs();
|
||||||
|
} catch (SlickException e) {
|
||||||
|
Log.warn("Failed to load glyphs.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -121,7 +121,7 @@ public class GameRanking extends BasicGameState {
|
||||||
// header text
|
// header text
|
||||||
g.setColor(Color.white);
|
g.setColor(Color.white);
|
||||||
Utils.FONT_LARGE.drawString(10, 0,
|
Utils.FONT_LARGE.drawString(10, 0,
|
||||||
String.format("%s - %s [%s]", osu.artist, osu.title, osu.version));
|
String.format("%s - %s [%s]", osu.getArtist(), osu.getTitle(), osu.version));
|
||||||
Utils.FONT_MEDIUM.drawString(10, Utils.FONT_LARGE.getLineHeight() - 6,
|
Utils.FONT_MEDIUM.drawString(10, Utils.FONT_LARGE.getLineHeight() - 6,
|
||||||
String.format("Beatmap by %s", osu.creator));
|
String.format("Beatmap by %s", osu.creator));
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,8 @@ public class Options extends BasicGameState {
|
||||||
CHECKPOINT,
|
CHECKPOINT,
|
||||||
DISABLE_SOUNDS,
|
DISABLE_SOUNDS,
|
||||||
KEY_LEFT,
|
KEY_LEFT,
|
||||||
KEY_RIGHT;
|
KEY_RIGHT,
|
||||||
|
SHOW_UNICODE;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,6 +170,7 @@ public class Options extends BasicGameState {
|
||||||
// GameOption.FULLSCREEN,
|
// GameOption.FULLSCREEN,
|
||||||
GameOption.TARGET_FPS,
|
GameOption.TARGET_FPS,
|
||||||
GameOption.SHOW_FPS,
|
GameOption.SHOW_FPS,
|
||||||
|
GameOption.SHOW_UNICODE,
|
||||||
GameOption.SCREENSHOT_FORMAT,
|
GameOption.SCREENSHOT_FORMAT,
|
||||||
GameOption.NEW_CURSOR,
|
GameOption.NEW_CURSOR,
|
||||||
GameOption.DYNAMIC_BACKGROUND,
|
GameOption.DYNAMIC_BACKGROUND,
|
||||||
|
@ -363,6 +365,11 @@ public class Options extends BasicGameState {
|
||||||
private static boolean disableSound =
|
private static boolean disableSound =
|
||||||
(System.getProperty("os.name").toLowerCase().indexOf("linux") > -1);
|
(System.getProperty("os.name").toLowerCase().indexOf("linux") > -1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to display non-English metadata.
|
||||||
|
*/
|
||||||
|
private static boolean showUnicode = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Left and right game keys.
|
* Left and right game keys.
|
||||||
*/
|
*/
|
||||||
|
@ -607,6 +614,18 @@ public class Options extends BasicGameState {
|
||||||
keyEntryLeft = false;
|
keyEntryLeft = false;
|
||||||
keyEntryRight = true;
|
keyEntryRight = true;
|
||||||
break;
|
break;
|
||||||
|
case SHOW_UNICODE:
|
||||||
|
showUnicode = !showUnicode;
|
||||||
|
if (showUnicode) {
|
||||||
|
try {
|
||||||
|
Utils.FONT_LARGE.loadGlyphs();
|
||||||
|
Utils.FONT_MEDIUM.loadGlyphs();
|
||||||
|
Utils.FONT_DEFAULT.loadGlyphs();
|
||||||
|
} catch (SlickException e) {
|
||||||
|
Log.warn("Failed to load glyphs.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -780,6 +799,12 @@ public class Options extends BasicGameState {
|
||||||
"Show an FPS counter in the bottom-right hand corner."
|
"Show an FPS counter in the bottom-right hand corner."
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case SHOW_UNICODE:
|
||||||
|
drawOption(pos, "Prefer Non-English Metadata",
|
||||||
|
showUnicode ? "Yes" : "No",
|
||||||
|
"Where available, song titles will be shown in their native language."
|
||||||
|
);
|
||||||
|
break;
|
||||||
case NEW_CURSOR:
|
case NEW_CURSOR:
|
||||||
drawOption(pos, "Enable New Cursor",
|
drawOption(pos, "Enable New Cursor",
|
||||||
newCursor ? "Yes" : "No",
|
newCursor ? "Yes" : "No",
|
||||||
|
@ -1132,6 +1157,12 @@ public class Options extends BasicGameState {
|
||||||
*/
|
*/
|
||||||
public static boolean isSoundDisabled() { return disableSound; }
|
public static boolean isSoundDisabled() { return disableSound; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not to use non-English metadata where available.
|
||||||
|
* @return true if Unicode preferred
|
||||||
|
*/
|
||||||
|
public static boolean useUnicodeMetadata() { return showUnicode; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the track checkpoint time, if within bounds.
|
* Sets the track checkpoint time, if within bounds.
|
||||||
* @param time the track position (in ms)
|
* @param time the track position (in ms)
|
||||||
|
@ -1288,6 +1319,9 @@ public class Options extends BasicGameState {
|
||||||
case "FpsCounter":
|
case "FpsCounter":
|
||||||
showFPS = Boolean.parseBoolean(value);
|
showFPS = Boolean.parseBoolean(value);
|
||||||
break;
|
break;
|
||||||
|
case "ShowUnicode":
|
||||||
|
showUnicode = Boolean.parseBoolean(value);
|
||||||
|
break;
|
||||||
case "NewCursor":
|
case "NewCursor":
|
||||||
newCursor = Boolean.parseBoolean(value);
|
newCursor = Boolean.parseBoolean(value);
|
||||||
break;
|
break;
|
||||||
|
@ -1416,6 +1450,8 @@ public class Options extends BasicGameState {
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
writer.write(String.format("FpsCounter = %b", showFPS));
|
writer.write(String.format("FpsCounter = %b", showFPS));
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
|
writer.write(String.format("ShowUnicode = %b", showUnicode));
|
||||||
|
writer.newLine();
|
||||||
writer.write(String.format("ScreenshotFormat = %d", screenshotFormatIndex));
|
writer.write(String.format("ScreenshotFormat = %d", screenshotFormatIndex));
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
writer.write(String.format("NewCursor = %b", newCursor));
|
writer.write(String.format("NewCursor = %b", newCursor));
|
||||||
|
|
|
@ -240,6 +240,7 @@ public class SongMenu extends BasicGameState {
|
||||||
OsuGroupNode node = startNode;
|
OsuGroupNode node = startNode;
|
||||||
for (int i = 0; i < MAX_BUTTONS && node != null; i++) {
|
for (int i = 0; i < MAX_BUTTONS && node != null; i++) {
|
||||||
node.draw(buttonX, buttonY + (i*buttonOffset), (node == focusNode));
|
node.draw(buttonX, buttonY + (i*buttonOffset), (node == focusNode));
|
||||||
|
Utils.loadGlyphs(node.osuFiles.get(0));
|
||||||
node = node.next;
|
node = node.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +570,9 @@ public class SongMenu extends BasicGameState {
|
||||||
if (flag || (startNode.index == 0 && startNode.osuFileIndex == -1 && startNode.prev == null))
|
if (flag || (startNode.index == 0 && startNode.osuFileIndex == -1 && startNode.prev == null))
|
||||||
startNode = node;
|
startNode = node;
|
||||||
focusNode = Opsu.groups.getNode(node, pos);
|
focusNode = Opsu.groups.getNode(node, pos);
|
||||||
MusicController.play(focusNode.osuFiles.get(focusNode.osuFileIndex), true);
|
OsuFile osu = focusNode.osuFiles.get(focusNode.osuFileIndex);
|
||||||
|
MusicController.play(osu, true);
|
||||||
|
Utils.loadGlyphs(osu);
|
||||||
|
|
||||||
// check startNode bounds
|
// check startNode bounds
|
||||||
while (startNode.index >= Opsu.groups.size() + length - MAX_BUTTONS && startNode.prev != null)
|
while (startNode.index >= Opsu.groups.size() + length - MAX_BUTTONS && startNode.prev != null)
|
||||||
|
|
|
@ -110,7 +110,6 @@ public class Splash extends BasicGameState {
|
||||||
// load other resources in a new thread
|
// load other resources in a new thread
|
||||||
final int width = container.getWidth();
|
final int width = container.getWidth();
|
||||||
final int height = container.getHeight();
|
final int height = container.getHeight();
|
||||||
final SongMenu menu = (SongMenu) game.getState(Opsu.STATE_SONGMENU);
|
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -122,10 +121,6 @@ public class Splash extends BasicGameState {
|
||||||
// parse song directory
|
// parse song directory
|
||||||
OsuParser.parseAllFiles(beatmapDir, width, height);
|
OsuParser.parseAllFiles(beatmapDir, width, height);
|
||||||
|
|
||||||
// initialize song list
|
|
||||||
Opsu.groups.init();
|
|
||||||
menu.setFocus(Opsu.groups.getRandomNode(), -1, true);
|
|
||||||
|
|
||||||
// load sounds
|
// load sounds
|
||||||
SoundController.init();
|
SoundController.init();
|
||||||
|
|
||||||
|
@ -140,9 +135,14 @@ public class Splash extends BasicGameState {
|
||||||
logo.setAlpha(alpha + (delta / 400f));
|
logo.setAlpha(alpha + (delta / 400f));
|
||||||
|
|
||||||
// change states when loading complete
|
// change states when loading complete
|
||||||
if (finished && alpha >= 1f)
|
if (finished && alpha >= 1f) {
|
||||||
|
// initialize song list
|
||||||
|
Opsu.groups.init();
|
||||||
|
((SongMenu) game.getState(Opsu.STATE_SONGMENU)).setFocus(Opsu.groups.getRandomNode(), -1, true);
|
||||||
|
|
||||||
game.enterState(Opsu.STATE_MAINMENU);
|
game.enterState(Opsu.STATE_MAINMENU);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getID() { return state; }
|
public int getID() { return state; }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user