nodeIter = nodes.iterator();
- while (nodeIter.hasNext()) {
- OsuGroupNode node = nodeIter.next();
- if (!node.matches(type, operator, value))
- nodeIter.remove();
- }
- }
-
- return true;
- }
-
- /**
- * Returns whether or not the list contains the given beatmap set ID.
- *
- * Note that IDs for older maps might have been improperly parsed, so
- * there is no guarantee that this method will return an accurate value.
- * @param id the beatmap set ID to check
- * @return true if id is in the list
- */
- public boolean containsBeatmapSetID(int id) { return MSIDdb.contains(id); }
-
- public OsuFile getFileFromBeatmapHash(String beatmapHash) {
- return beatmapHashesToFile.get(beatmapHash);
- }
-}
\ No newline at end of file
diff --git a/src/itdelatrisu/opsu/OsuHitObject.java b/src/itdelatrisu/opsu/OsuHitObject.java
deleted file mode 100644
index 4b62bdce..00000000
--- a/src/itdelatrisu/opsu/OsuHitObject.java
+++ /dev/null
@@ -1,543 +0,0 @@
-//TODO rename
-
-/*
- * opsu! - an open-source osu! client
- * Copyright (C) 2014, 2015 Jeffrey Han
- *
- * opsu! is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * opsu! is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with opsu!. If not, see .
- */
-
-package itdelatrisu.opsu;
-
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-
-/**
- * Data type representing a hit object.
- */
-public class OsuHitObject {
- /** Hit object types (bits). */
- public static final int
- TYPE_CIRCLE = 1,
- TYPE_SLIDER = 2,
- TYPE_NEWCOMBO = 4, // not an object
- TYPE_SPINNER = 8;
-
- /** Hit object type names. */
- private static final String
- CIRCLE = "circle",
- SLIDER = "slider",
- SPINNER = "spinner",
- UNKNOWN = "unknown object";
-
- /** Hit sound types (bits). */
- public static final byte
- SOUND_NORMAL = 0,
- SOUND_WHISTLE = 2,
- SOUND_FINISH = 4,
- SOUND_CLAP = 8;
-
- /**
- * Slider curve types.
- * (Deprecated: only Beziers are currently used.)
- */
- public static final char
- SLIDER_CATMULL = 'C',
- SLIDER_BEZIER = 'B',
- SLIDER_LINEAR = 'L',
- SLIDER_PASSTHROUGH = 'P';
-
- /** Max hit object coordinates. */
- private static final int
- MAX_X = 512,
- MAX_Y = 384;
-
- /** The x and y multipliers for hit object coordinates. */
- private static float xMultiplier, yMultiplier;
-
- /** The x and y offsets for hit object coordinates. */
- private static int
- xOffset, // offset right of border
- yOffset; // offset below health bar
-
- /** The container height. */
- private static int containerHeight;
-
- /** The offset per stack. */
- private static float stackOffset;
-
- /**
- * Returns the stack position modifier, in pixels.
- * @return stack position modifier
- */
- public static float getStackOffset() { return stackOffset; }
-
- /**
- * Sets the stack position modifier.
- * @param offset stack position modifier, in pixels
- */
- public static void setStackOffset(float offset) { stackOffset = offset; }
-
- /** Starting coordinates. */
- private float x, y;
-
- /** Start time (in ms). */
- private int time;
-
- /** Hit object type (TYPE_* bitmask). */
- private int type;
-
- /** Hit sound type (SOUND_* bitmask). */
- private byte hitSound;
-
- /** Hit sound addition (sampleSet, AdditionSampleSet, ?, ...). */
- private byte[] addition;
-
- /** Slider curve type (SLIDER_* constant). */
- private char sliderType;
-
- /** Slider coordinate lists. */
- private float[] sliderX, sliderY;
-
- /** Slider repeat count. */
- private int repeat;
-
- /** Slider pixel length. */
- private float pixelLength;
-
- /** Spinner end time (in ms). */
- private int endTime;
-
- /** Slider edge hit sound type (SOUND_* bitmask). */
- private byte[] edgeHitSound;
-
- /** Slider edge hit sound addition (sampleSet, AdditionSampleSet). */
- private byte[][] edgeAddition;
-
- /** Current index in combo color array. */
- private int comboIndex;
-
- /** Number to display in hit object. */
- private int comboNumber;
-
- /** Hit object index in the current stack. */
- private int stack;
-
- /**
- * Initializes the OsuHitObject data type with container dimensions.
- * @param width the container width
- * @param height the container height
- */
- public static void init(int width, int height) {
- containerHeight = height;
- int swidth = width;
- int sheight = height;
- if (swidth * 3 > sheight * 4)
- swidth = sheight * 4 / 3;
- else
- sheight = swidth * 3 / 4;
- xMultiplier = swidth / 640f;
- yMultiplier = sheight / 480f;
-
- //xMultiplier = swidth / MAX_X;
- //yMultiplier = sheight / MAX_Y;
- xOffset = (int) (width - MAX_X * xMultiplier) / 2;
- yOffset = (int) (height - MAX_Y * yMultiplier) / 2;
- }
-
- /**
- * Returns the X multiplier for coordinates.
- */
- public static float getXMultiplier() { return xMultiplier; }
-
- /**
- * Returns the Y multiplier for coordinates.
- */
- public static float getYMultiplier() { return yMultiplier; }
-
- /**
- * Returns the X offset for coordinates.
- */
- public static int getXOffset() { return xOffset; }
-
- /**
- * Returns the Y offset for coordinates.
- */
- public static int getYOffset() { return yOffset; }
-
- /**
- * Constructor.
- * @param line the line to be parsed
- */
- public OsuHitObject(String line) {
- /**
- * [OBJECT FORMATS]
- * Circles:
- * x,y,time,type,hitSound,addition
- * 256,148,9466,1,2,0:0:0:0:
- *
- * Sliders:
- * x,y,time,type,hitSound,sliderType|curveX:curveY|...,repeat,pixelLength,edgeHitsound,edgeAddition,addition
- * 300,68,4591,2,0,B|372:100|332:172|420:192,2,180,2|2|2,0:0|0:0|0:0,0:0:0:0:
- *
- * Spinners:
- * x,y,time,type,hitSound,endTime,addition
- * 256,192,654,12,0,4029,0:0:0:0:
- *
- * NOTE: 'addition' -> sampl:add:cust:vol:hitsound (optional, defaults to "0:0:0:0:")
- */
- String tokens[] = line.split(",");
-
- // common fields
- this.x = Float.parseFloat(tokens[0]);
- this.y = Float.parseFloat(tokens[1]);
- this.time = Integer.parseInt(tokens[2]);
- this.type = Integer.parseInt(tokens[3]);
- this.hitSound = Byte.parseByte(tokens[4]);
-
- // type-specific fields
- int additionIndex;
- if ((type & OsuHitObject.TYPE_CIRCLE) > 0)
- additionIndex = 5;
- else if ((type & OsuHitObject.TYPE_SLIDER) > 0) {
- additionIndex = 10;
-
- // slider curve type and coordinates
- String[] sliderTokens = tokens[5].split("\\|");
- this.sliderType = sliderTokens[0].charAt(0);
- this.sliderX = new float[sliderTokens.length - 1];
- this.sliderY = new float[sliderTokens.length - 1];
- for (int j = 1; j < sliderTokens.length; j++) {
- String[] sliderXY = sliderTokens[j].split(":");
- this.sliderX[j - 1] = Integer.parseInt(sliderXY[0]);
- this.sliderY[j - 1] = Integer.parseInt(sliderXY[1]);
- }
- this.repeat = Integer.parseInt(tokens[6]);
- this.pixelLength = Float.parseFloat(tokens[7]);
- if (tokens.length > 8) {
- String[] edgeHitSoundTokens = tokens[8].split("\\|");
- this.edgeHitSound = new byte[edgeHitSoundTokens.length];
- for (int j = 0; j < edgeHitSoundTokens.length; j++)
- edgeHitSound[j] = Byte.parseByte(edgeHitSoundTokens[j]);
- }
- if (tokens.length > 9) {
- String[] edgeAdditionTokens = tokens[9].split("\\|");
- this.edgeAddition = new byte[edgeAdditionTokens.length][2];
- for (int j = 0; j < edgeAdditionTokens.length; j++) {
- String[] tedgeAddition = edgeAdditionTokens[j].split(":");
- edgeAddition[j][0] = Byte.parseByte(tedgeAddition[0]);
- edgeAddition[j][1] = Byte.parseByte(tedgeAddition[1]);
- }
- }
- } else { //if ((type & OsuHitObject.TYPE_SPINNER) > 0) {
- additionIndex = 6;
-
- // some 'endTime' fields contain a ':' character (?)
- int index = tokens[5].indexOf(':');
- if (index != -1)
- tokens[5] = tokens[5].substring(0, index);
- this.endTime = Integer.parseInt(tokens[5]);
- }
-
- // addition
- if (tokens.length > additionIndex) {
- String[] additionTokens = tokens[additionIndex].split(":");
- this.addition = new byte[additionTokens.length];
- for (int j = 0; j < additionTokens.length; j++)
- this.addition[j] = Byte.parseByte(additionTokens[j]);
- }
- }
-
- /**
- * Returns the raw starting x coordinate.
- */
- public float getX() { return x; }
-
- /**
- * Returns the raw starting y coordinate.
- */
- public float getY() { return y; }
-
- /**
- * Returns the scaled starting x coordinate.
- */
- public float getScaledX() { return (x - stack * stackOffset) * xMultiplier + xOffset; }
-
- /**
- * Returns the scaled starting y coordinate.
- */
- public float getScaledY() {
- if (GameMod.HARD_ROCK.isActive())
- return containerHeight - ((y - stack * stackOffset) * yMultiplier + yOffset);
- else
- return (y - stack * stackOffset) * yMultiplier + yOffset;
- }
-
- /**
- * Returns the start time.
- * @return the start time (in ms)
- */
- public int getTime() { return time; }
-
- /**
- * Returns the hit object type.
- * @return the object type (TYPE_* bitmask)
- */
- public int getType() { return type; }
-
- /**
- * Returns the name of the hit object type.
- */
- public String getTypeName() {
- if (isCircle())
- return CIRCLE;
- else if (isSlider())
- return SLIDER;
- else if (isSpinner())
- return SPINNER;
- else
- return UNKNOWN;
- }
-
- /**
- * Returns the hit sound type.
- * @return the sound type (SOUND_* bitmask)
- */
- public byte getHitSoundType() { return hitSound; }
-
- /**
- * Returns the edge hit sound type.
- * @param index the slider edge index (ignored for non-sliders)
- * @return the sound type (SOUND_* bitmask)
- */
- public byte getEdgeHitSoundType(int index) {
- if (edgeHitSound != null)
- return edgeHitSound[index];
- else
- return hitSound;
- }
-
- /**
- * Returns the slider type.
- * @return the slider type (SLIDER_* constant)
- */
- public char getSliderType() { return sliderType; }
-
- /**
- * Returns a list of raw slider x coordinates.
- */
- public float[] getSliderX() { return sliderX; }
-
- /**
- * Returns a list of raw slider y coordinates.
- */
- public float[] getSliderY() { return sliderY; }
-
- /**
- * Returns a list of scaled slider x coordinates.
- * Note that this method will create a new array.
- */
- public float[] getScaledSliderX() {
- if (sliderX == null)
- return null;
-
- float[] x = new float[sliderX.length];
- for (int i = 0; i < x.length; i++)
- x[i] = (sliderX[i] - stack * stackOffset) * xMultiplier + xOffset;
- return x;
- }
-
- /**
- * Returns a list of scaled slider y coordinates.
- * Note that this method will create a new array.
- */
- public float[] getScaledSliderY() {
- if (sliderY == null)
- return null;
-
- float[] y = new float[sliderY.length];
- if (GameMod.HARD_ROCK.isActive()) {
- for (int i = 0; i < y.length; i++)
- y[i] = containerHeight - ((sliderY[i] - stack * stackOffset) * yMultiplier + yOffset);
- } else {
- for (int i = 0; i < y.length; i++)
- y[i] = (sliderY[i] - stack * stackOffset) * yMultiplier + yOffset;
- }
- return y;
- }
-
- /**
- * Returns the slider repeat count.
- * @return the repeat count
- */
- public int getRepeatCount() { return repeat; }
-
- /**
- * Returns the slider pixel length.
- * @return the pixel length
- */
- public float getPixelLength() { return pixelLength; }
-
- /**
- * Returns the spinner end time.
- * @return the end time (in ms)
- */
- public int getEndTime() { return endTime; }
-
- /**
- * Sets the current index in the combo color array.
- * @param comboIndex the combo index
- */
- public void setComboIndex(int comboIndex) { this.comboIndex = comboIndex; }
-
- /**
- * Returns the current index in the combo color array.
- * @return the combo index
- */
- public int getComboIndex() { return comboIndex; }
-
- /**
- * Sets the number to display in the hit object.
- * @param comboNumber the combo number
- */
- public void setComboNumber(int comboNumber) { this.comboNumber = comboNumber; }
-
- /**
- * Returns the number to display in the hit object.
- * @return the combo number
- */
- public int getComboNumber() { return comboNumber; }
-
- /**
- * Returns whether or not the hit object is a circle.
- * @return true if circle
- */
- public boolean isCircle() { return (type & TYPE_CIRCLE) > 0; }
-
- /**
- * Returns whether or not the hit object is a slider.
- * @return true if slider
- */
- public boolean isSlider() { return (type & TYPE_SLIDER) > 0; }
-
- /**
- * Returns whether or not the hit object is a spinner.
- * @return true if spinner
- */
- public boolean isSpinner() { return (type & TYPE_SPINNER) > 0; }
-
- /**
- * Returns whether or not the hit object starts a new combo.
- * @return true if new combo
- */
- public boolean isNewCombo() { return (type & TYPE_NEWCOMBO) > 0; }
-
- /**
- * Returns the number of extra skips on the combo colors.
- */
- public int getComboSkip() { return (type >> TYPE_NEWCOMBO); }
-
- /**
- * Returns the sample set at the given index.
- * @param index the index (for sliders, ignored otherwise)
- * @return the sample set, or 0 if none available
- */
- public byte getSampleSet(int index) {
- if (edgeAddition != null)
- return edgeAddition[index][0];
- if (addition != null)
- return addition[0];
- return 0;
- }
-
- /**
- * Returns the 'addition' sample set at the given index.
- * @param index the index (for sliders, ignored otherwise)
- * @return the sample set, or 0 if none available
- */
- public byte getAdditionSampleSet(int index) {
- if (edgeAddition != null)
- return edgeAddition[index][1];
- if (addition != null)
- return addition[1];
- return 0;
- }
-
- /**
- * Sets the hit object index in the current stack.
- * @param stack index in the stack
- */
- public void setStack(int stack) { this.stack = stack; }
-
- /**
- * Returns the hit object index in the current stack.
- * @return index in the stack
- */
- public int getStack() { return stack; }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- NumberFormat nf = new DecimalFormat("###.#####");
-
- // common fields
- sb.append(nf.format(x)); sb.append(',');
- sb.append(nf.format(y)); sb.append(',');
- sb.append(time); sb.append(',');
- sb.append(type); sb.append(',');
- sb.append(hitSound); sb.append(',');
-
- // type-specific fields
- if (isCircle())
- ;
- else if (isSlider()) {
- sb.append(getSliderType());
- sb.append('|');
- for (int i = 0; i < sliderX.length; i++) {
- sb.append(nf.format(sliderX[i])); sb.append(':');
- sb.append(nf.format(sliderY[i])); sb.append('|');
- }
- sb.setCharAt(sb.length() - 1, ',');
- sb.append(repeat); sb.append(',');
- sb.append(pixelLength); sb.append(',');
- if (edgeHitSound != null) {
- for (int i = 0; i < edgeHitSound.length; i++) {
- sb.append(edgeHitSound[i]); sb.append('|');
- }
- sb.setCharAt(sb.length() - 1, ',');
- }
- if (edgeAddition != null) {
- for (int i = 0; i < edgeAddition.length; i++) {
- sb.append(edgeAddition[i][0]); sb.append(':');
- sb.append(edgeAddition[i][1]); sb.append('|');
- }
- sb.setCharAt(sb.length() - 1, ',');
- }
- } else if (isSpinner()) {
- sb.append(endTime);
- sb.append(',');
- }
-
- // addition
- if (addition != null) {
- for (int i = 0; i < addition.length; i++) {
- sb.append(addition[i]);
- sb.append(':');
- }
- } else
- sb.setLength(sb.length() - 1);
-
- return sb.toString();
- }
-}
diff --git a/src/itdelatrisu/opsu/OsuParser.java b/src/itdelatrisu/opsu/OsuParser.java
deleted file mode 100644
index 715e90d4..00000000
--- a/src/itdelatrisu/opsu/OsuParser.java
+++ /dev/null
@@ -1,742 +0,0 @@
-//TODO rename
-
-/*
- * opsu! - an open-source osu! client
- * Copyright (C) 2014, 2015 Jeffrey Han
- *
- * opsu! is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * opsu! is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with opsu!. If not, see .
- */
-
-package itdelatrisu.opsu;
-
-import itdelatrisu.opsu.db.OsuDB;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import org.newdawn.slick.Color;
-import org.newdawn.slick.util.Log;
-
-/**
- * Parser for OSU files.
- */
-public class OsuParser {
- /** The string lookup database. */
- private static HashMap stringdb = new HashMap();
-
- /** The expected pattern for beatmap directories, used to find beatmap set IDs. */
- private static final String DIR_MSID_PATTERN = "^\\d+ .*";
-
- /** The current file being parsed. */
- private static File currentFile;
-
- /** The current directory number while parsing. */
- private static int currentDirectoryIndex = -1;
-
- /** The total number of directories to parse. */
- private static int totalDirectories = -1;
-
- /** Parser statuses. */
- public enum Status { NONE, PARSING, CACHE, INSERTING };
-
- /** The current status. */
- private static Status status = Status.NONE;
-
- // This class should not be instantiated.
- private OsuParser() {}
-
- /**
- * Invokes parser for each OSU file in a root directory and
- * adds the OsuFiles to a new OsuGroupList.
- * @param root the root directory (search has depth 1)
- */
- public static void parseAllFiles(File root) {
- // create a new OsuGroupList
- OsuGroupList.create();
-
- // parse all directories
- parseDirectories(root.listFiles());
- }
-
- /**
- * Invokes parser for each directory in the given array and
- * adds the OsuFiles to the existing OsuGroupList.
- * @param dirs the array of directories to parse
- * @return the last OsuGroupNode parsed, or null if none
- */
- public static OsuGroupNode parseDirectories(File[] dirs) {
- if (dirs == null)
- return null;
-
- // progress tracking
- status = Status.PARSING;
- currentDirectoryIndex = 0;
- totalDirectories = dirs.length;
-
- // get last modified map from database
- Map map = OsuDB.getLastModifiedMap();
-
- // OsuFile lists
- List> allOsuFiles = new LinkedList>();
- List cachedOsuFiles = new LinkedList(); // loaded from database
- List parsedOsuFiles = new LinkedList(); // loaded from parser
-
- // parse directories
- OsuGroupNode lastNode = null;
- for (File dir : dirs) {
- currentDirectoryIndex++;
- if (!dir.isDirectory())
- continue;
-
- // find all OSU files
- File[] files = dir.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.toLowerCase().endsWith(".osu");
- }
- });
- if (files.length < 1)
- continue;
-
- // create a new group entry
- ArrayList osuFiles = new ArrayList();
- for (File file : files) {
- currentFile = file;
-
- // check if beatmap is cached
- String path = String.format("%s/%s", dir.getName(), file.getName());
- if (map.containsKey(path)) {
- // check last modified times
- long lastModified = map.get(path);
- if (lastModified == file.lastModified()) {
- // add to cached beatmap list
- OsuFile osu = new OsuFile(file);
- osuFiles.add(osu);
- cachedOsuFiles.add(osu);
- continue;
- } else
- OsuDB.delete(dir.getName(), file.getName());
- }
-
- // Parse hit objects only when needed to save time/memory.
- // Change boolean to 'true' to parse them immediately.
- OsuFile osu = parseFile(file, dir, osuFiles, false);
-
- // add to parsed beatmap list
- if (osu != null) {
- osuFiles.add(osu);
- parsedOsuFiles.add(osu);
- }
- }
-
- // add group entry if non-empty
- if (!osuFiles.isEmpty()) {
- osuFiles.trimToSize();
- allOsuFiles.add(osuFiles);
- }
-
- // stop parsing files (interrupted)
- if (Thread.interrupted())
- break;
- }
-
- // load cached entries from database
- if (!cachedOsuFiles.isEmpty()) {
- status = Status.CACHE;
-
- // Load array fields only when needed to save time/memory.
- // Change flag to 'LOAD_ALL' to load them immediately.
- OsuDB.load(cachedOsuFiles, OsuDB.LOAD_NONARRAY);
- }
-
- // add group entries to OsuGroupList
- for (ArrayList osuFiles : allOsuFiles) {
- Collections.sort(osuFiles);
- lastNode = OsuGroupList.get().addSongGroup(osuFiles);
- }
-
- // clear string DB
- stringdb = new HashMap();
-
- // add beatmap entries to database
- if (!parsedOsuFiles.isEmpty()) {
- status = Status.INSERTING;
- OsuDB.insert(parsedOsuFiles);
- }
-
- status = Status.NONE;
- currentFile = null;
- currentDirectoryIndex = -1;
- totalDirectories = -1;
- return lastNode;
- }
-
- /**
- * Parses an OSU file.
- * @param file the file to parse
- * @param dir the directory containing the beatmap
- * @param osuFiles the song group
- * @param parseObjects if true, hit objects will be fully parsed now
- * @return the new OsuFile object
- */
- private static OsuFile parseFile(File file, File dir, ArrayList osuFiles, boolean parseObjects) {
- OsuFile osu = new OsuFile(file);
- osu.timingPoints = new ArrayList();
-
- try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
- String line = in.readLine();
- String tokens[] = null;
- while (line != null) {
- line = line.trim();
- if (!isValidLine(line)) {
- line = in.readLine();
- continue;
- }
- switch (line) {
- case "[General]":
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- if ((tokens = tokenize(line)) == null)
- continue;
- try {
- switch (tokens[0]) {
- case "AudioFilename":
- File audioFileName = new File(dir, tokens[1]);
- if (!osuFiles.isEmpty()) {
- // if possible, reuse the same File object from another OsuFile in the group
- File groupAudioFileName = osuFiles.get(0).audioFilename;
- if (groupAudioFileName != null &&
- tokens[1].equalsIgnoreCase(groupAudioFileName.getName()))
- audioFileName = groupAudioFileName;
- }
- if (!audioFileName.isFile()) {
- // try to find the file with a case-insensitive match
- boolean match = false;
- for (String s : dir.list()) {
- if (s.equalsIgnoreCase(tokens[1])) {
- audioFileName = new File(dir, s);
- match = true;
- break;
- }
- }
- if (!match) {
- Log.error(String.format("File not found: '%s'", tokens[1]));
- return null;
- }
- }
- osu.audioFilename = audioFileName;
- break;
- case "AudioLeadIn":
- osu.audioLeadIn = Integer.parseInt(tokens[1]);
- break;
-// case "AudioHash": // deprecated
-// osu.audioHash = tokens[1];
-// break;
- case "PreviewTime":
- osu.previewTime = Integer.parseInt(tokens[1]);
- break;
- case "Countdown":
- osu.countdown = Byte.parseByte(tokens[1]);
- break;
- case "SampleSet":
- osu.sampleSet = getDBString(tokens[1]);
- break;
- case "StackLeniency":
- osu.stackLeniency = Float.parseFloat(tokens[1]);
- break;
- case "Mode":
- osu.mode = Byte.parseByte(tokens[1]);
-
- /* Non-Opsu! standard files not implemented (obviously). */
- if (osu.mode != OsuFile.MODE_OSU)
- return null;
-
- break;
- case "LetterboxInBreaks":
- osu.letterboxInBreaks = (Integer.parseInt(tokens[1]) == 1);
- break;
- case "WidescreenStoryboard":
- osu.widescreenStoryboard = (Integer.parseInt(tokens[1]) == 1);
- break;
- case "EpilepsyWarning":
- osu.epilepsyWarning = (Integer.parseInt(tokens[1]) == 1);
- default:
- break;
- }
- } catch (Exception e) {
- Log.warn(String.format("Failed to read line '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- }
- break;
- case "[Editor]":
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- /* Not implemented. */
-// if ((tokens = tokenize(line)) == null)
-// continue;
-// try {
-// switch (tokens[0]) {
-// case "Bookmarks":
-// String[] bookmarks = tokens[1].split(",");
-// osu.bookmarks = new int[bookmarks.length];
-// for (int i = 0; i < bookmarks.length; i++)
-// osu.bookmarks[i] = Integer.parseInt(bookmarks[i]);
-// break;
-// case "DistanceSpacing":
-// osu.distanceSpacing = Float.parseFloat(tokens[1]);
-// break;
-// case "BeatDivisor":
-// osu.beatDivisor = Byte.parseByte(tokens[1]);
-// break;
-// case "GridSize":
-// osu.gridSize = Integer.parseInt(tokens[1]);
-// break;
-// case "TimelineZoom":
-// osu.timelineZoom = Integer.parseInt(tokens[1]);
-// break;
-// default:
-// break;
-// }
-// } catch (Exception e) {
-// Log.warn(String.format("Failed to read editor line '%s' for file '%s'.",
-// line, file.getAbsolutePath()), e);
-// }
- }
- break;
- case "[Metadata]":
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- if ((tokens = tokenize(line)) == null)
- continue;
- try {
- switch (tokens[0]) {
- case "Title":
- osu.title = getDBString(tokens[1]);
- break;
- case "TitleUnicode":
- osu.titleUnicode = getDBString(tokens[1]);
- break;
- case "Artist":
- osu.artist = getDBString(tokens[1]);
- break;
- case "ArtistUnicode":
- osu.artistUnicode = getDBString(tokens[1]);
- break;
- case "Creator":
- osu.creator = getDBString(tokens[1]);
- break;
- case "Version":
- osu.version = getDBString(tokens[1]);
- break;
- case "Source":
- osu.source = getDBString(tokens[1]);
- break;
- case "Tags":
- osu.tags = getDBString(tokens[1].toLowerCase());
- break;
- case "BeatmapID":
- osu.beatmapID = Integer.parseInt(tokens[1]);
- break;
- case "BeatmapSetID":
- osu.beatmapSetID = Integer.parseInt(tokens[1]);
- break;
- }
- } catch (Exception e) {
- Log.warn(String.format("Failed to read metadata '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- if (osu.beatmapSetID <= 0) { // try to determine MSID from directory name
- if (dir != null && dir.isDirectory()) {
- String dirName = dir.getName();
- if (!dirName.isEmpty() && dirName.matches(DIR_MSID_PATTERN))
- osu.beatmapSetID = Integer.parseInt(dirName.substring(0, dirName.indexOf(' ')));
- }
- }
- }
- break;
- case "[Difficulty]":
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- if ((tokens = tokenize(line)) == null)
- continue;
- try {
- switch (tokens[0]) {
- case "HPDrainRate":
- osu.HPDrainRate = Float.parseFloat(tokens[1]);
- break;
- case "CircleSize":
- osu.circleSize = Float.parseFloat(tokens[1]);
- break;
- case "OverallDifficulty":
- osu.overallDifficulty = Float.parseFloat(tokens[1]);
- break;
- case "ApproachRate":
- osu.approachRate = Float.parseFloat(tokens[1]);
- break;
- case "SliderMultiplier":
- osu.sliderMultiplier = Float.parseFloat(tokens[1]);
- break;
- case "SliderTickRate":
- osu.sliderTickRate = Float.parseFloat(tokens[1]);
- break;
- }
- } catch (Exception e) {
- Log.warn(String.format("Failed to read difficulty '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- }
- if (osu.approachRate == -1f) // not in old format
- osu.approachRate = osu.overallDifficulty;
- break;
- case "[Events]":
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- tokens = line.split(",");
- switch (tokens[0]) {
- case "0": // background
- tokens[2] = tokens[2].replaceAll("^\"|\"$", "");
- String ext = OsuParser.getExtension(tokens[2]);
- if (ext.equals("jpg") || ext.equals("png"))
- osu.bg = getDBString(tokens[2]);
- break;
- case "2": // break periods
- try {
- if (osu.breaks == null) // optional, create if needed
- osu.breaks = new ArrayList();
- osu.breaks.add(Integer.parseInt(tokens[1]));
- osu.breaks.add(Integer.parseInt(tokens[2]));
- } catch (Exception e) {
- Log.warn(String.format("Failed to read break period '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- break;
- default:
- /* Not implemented. */
- break;
- }
- }
- if (osu.breaks != null)
- osu.breaks.trimToSize();
- break;
- case "[TimingPoints]":
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
-
- try {
- // parse timing point
- OsuTimingPoint timingPoint = new OsuTimingPoint(line);
-
- // calculate BPM
- if (!timingPoint.isInherited()) {
- int bpm = Math.round(60000 / timingPoint.getBeatLength());
- if (osu.bpmMin == 0)
- osu.bpmMin = osu.bpmMax = bpm;
- else if (bpm < osu.bpmMin)
- osu.bpmMin = bpm;
- else if (bpm > osu.bpmMax)
- osu.bpmMax = bpm;
- }
-
- osu.timingPoints.add(timingPoint);
- } catch (Exception e) {
- Log.warn(String.format("Failed to read timing point '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- }
- osu.timingPoints.trimToSize();
- break;
- case "[Colours]":
- LinkedList colors = new LinkedList();
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- if ((tokens = tokenize(line)) == null)
- continue;
- try {
- switch (tokens[0]) {
- case "Combo1":
- case "Combo2":
- case "Combo3":
- case "Combo4":
- case "Combo5":
- case "Combo6":
- case "Combo7":
- case "Combo8":
- String[] rgb = tokens[1].split(",");
- colors.add(new Color(
- Integer.parseInt(rgb[0]),
- Integer.parseInt(rgb[1]),
- Integer.parseInt(rgb[2])
- ));
- default:
- break;
- }
- } catch (Exception e) {
- Log.warn(String.format("Failed to read color '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- }
- if (!colors.isEmpty())
- osu.combo = colors.toArray(new Color[colors.size()]);
- break;
- case "[HitObjects]":
- int type = 0;
- while ((line = in.readLine()) != null) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
- /* Only type counts parsed at this time. */
- tokens = line.split(",");
- try {
- type = Integer.parseInt(tokens[3]);
- if ((type & OsuHitObject.TYPE_CIRCLE) > 0)
- osu.hitObjectCircle++;
- else if ((type & OsuHitObject.TYPE_SLIDER) > 0)
- osu.hitObjectSlider++;
- else //if ((type & OsuHitObject.TYPE_SPINNER) > 0)
- osu.hitObjectSpinner++;
- } catch (Exception e) {
- Log.warn(String.format("Failed to read hit object '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- }
-
- try {
- // map length = last object end time (TODO: end on slider?)
- if ((type & OsuHitObject.TYPE_SPINNER) > 0) {
- // some 'endTime' fields contain a ':' character (?)
- int index = tokens[5].indexOf(':');
- if (index != -1)
- tokens[5] = tokens[5].substring(0, index);
- osu.endTime = Integer.parseInt(tokens[5]);
- } else if (type != 0)
- osu.endTime = Integer.parseInt(tokens[2]);
- } catch (Exception e) {
- Log.warn(String.format("Failed to read hit object end time '%s' for file '%s'.",
- line, file.getAbsolutePath()), e);
- }
- break;
- default:
- line = in.readLine();
- break;
- }
- }
- } catch (IOException e) {
- ErrorHandler.error(String.format("Failed to read file '%s'.", file.getAbsolutePath()), e, false);
- }
-
- // no associated audio file?
- if (osu.audioFilename == null)
- return null;
-
- // if no custom colors, use the default color scheme
- if (osu.combo == null)
- osu.combo = Utils.DEFAULT_COMBO;
-
- // parse hit objects now?
- if (parseObjects)
- parseHitObjects(osu);
-
- osu.md5Hash = Utils.getMD5(file);
-
- return osu;
- }
-
- /**
- * Parses all hit objects in an OSU file.
- * @param osu the OsuFile to parse
- */
- public static void parseHitObjects(OsuFile osu) {
- if (osu.objects != null) // already parsed
- return;
-
- osu.objects = new OsuHitObject[(osu.hitObjectCircle
- + osu.hitObjectSlider + osu.hitObjectSpinner)];
-
- try (BufferedReader in = new BufferedReader(new FileReader(osu.getFile()))) {
- String line = in.readLine();
- while (line != null) {
- line = line.trim();
- if (!line.equals("[HitObjects]"))
- line = in.readLine();
- else
- break;
- }
- if (line == null) {
- Log.warn(String.format("No hit objects found in OsuFile '%s'.", osu.toString()));
- return;
- }
-
- // combo info
- int comboIndex = 0; // color index
- int comboNumber = 1; // combo number
-
- int objectIndex = 0;
- boolean first = true;
- while ((line = in.readLine()) != null && objectIndex < osu.objects.length) {
- line = line.trim();
- if (!isValidLine(line))
- continue;
- if (line.charAt(0) == '[')
- break;
-
- // lines must have at minimum 5 parameters
- int tokenCount = line.length() - line.replace(",", "").length();
- if (tokenCount < 4)
- continue;
-
- try {
- // create a new OsuHitObject for each line
- OsuHitObject hitObject = new OsuHitObject(line);
-
- // set combo info
- // - new combo: get next combo index, reset combo number
- // - else: maintain combo index, increase combo number
- if (hitObject.isNewCombo() || first) {
- int skip = (hitObject.isSpinner() ? 0 : 1) + hitObject.getComboSkip();
- for (int i = 0; i < skip; i++) {
- comboIndex = (comboIndex + 1) % osu.combo.length;
- comboNumber = 1;
- }
- first = false;
- }
-
- hitObject.setComboIndex(comboIndex);
- hitObject.setComboNumber(comboNumber++);
-
- osu.objects[objectIndex++] = hitObject;
- } catch (Exception e) {
- Log.warn(String.format("Failed to read hit object '%s' for OsuFile '%s'.",
- line, osu.toString()), e);
- }
- }
- } catch (IOException e) {
- ErrorHandler.error(String.format("Failed to read file '%s'.", osu.getFile().getAbsolutePath()), e, false);
- }
- }
-
- /**
- * Returns false if the line is too short or commented.
- */
- private static boolean isValidLine(String line) {
- return (line.length() > 1 && !line.startsWith("//"));
- }
-
- /**
- * Splits line into two strings: tag, value.
- * If no ':' character is present, null will be returned.
- */
- private static String[] tokenize(String line) {
- int index = line.indexOf(':');
- if (index == -1) {
- Log.debug(String.format("Failed to tokenize line: '%s'.", line));
- return null;
- }
-
- String[] tokens = new String[2];
- tokens[0] = line.substring(0, index).trim();
- tokens[1] = line.substring(index + 1).trim();
- return tokens;
- }
-
- /**
- * Returns the file extension of a file.
- */
- public static String getExtension(String file) {
- int i = file.lastIndexOf('.');
- return (i != -1) ? file.substring(i + 1).toLowerCase() : "";
- }
-
- /**
- * Returns the name of the current file being parsed, or null if none.
- */
- public static String getCurrentFileName() {
- if (status == Status.PARSING)
- return (currentFile != null) ? currentFile.getName() : null;
- else
- return (status == Status.NONE) ? null : "";
- }
-
- /**
- * Returns the progress of file parsing, or -1 if not parsing.
- * @return the completion percent [0, 100] or -1
- */
- public static int getParserProgress() {
- if (currentDirectoryIndex == -1 || totalDirectories == -1)
- return -1;
-
- return currentDirectoryIndex * 100 / totalDirectories;
- }
-
- /**
- * Returns the current parser status.
- */
- public static Status getStatus() { return status; }
-
- /**
- * Returns the String object in the database for the given String.
- * If none, insert the String into the database and return the original String.
- * @param s the string to retrieve
- * @return the string object
- */
- public static String getDBString(String s) {
- String DBString = stringdb.get(s);
- if (DBString == null) {
- stringdb.put(s, s);
- return s;
- } else
- return DBString;
- }
-}
\ No newline at end of file
diff --git a/src/itdelatrisu/opsu/UI.java b/src/itdelatrisu/opsu/UI.java
deleted file mode 100644
index 1f7aa07d..00000000
--- a/src/itdelatrisu/opsu/UI.java
+++ /dev/null
@@ -1,722 +0,0 @@
-//TODO rename?
-
-/*
- * opsu! - an open-source osu! client
- * Copyright (C) 2014, 2015 Jeffrey Han
- *
- * opsu! is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * opsu! is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with opsu!. If not, see .
- */
-
-package itdelatrisu.opsu;
-
-import itdelatrisu.opsu.audio.SoundController;
-
-import java.nio.IntBuffer;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-import javax.swing.JOptionPane;
-import javax.swing.UIManager;
-
-import org.lwjgl.BufferUtils;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.input.Cursor;
-import org.newdawn.slick.Animation;
-import org.newdawn.slick.Color;
-import org.newdawn.slick.GameContainer;
-import org.newdawn.slick.Graphics;
-import org.newdawn.slick.Image;
-import org.newdawn.slick.Input;
-import org.newdawn.slick.SlickException;
-import org.newdawn.slick.state.StateBasedGame;
-
-/**
- * Class primarily used for drawing UI components.
- */
-public class UI {
- /** Back button. */
- private static MenuButton backButton;
-
- /** Empty cursor. */
- private static Cursor emptyCursor;
-
- /** Last cursor coordinates. */
- private static int lastX = -1, lastY = -1;
-
- /** Cursor rotation angle. */
- private static float cursorAngle = 0f;
-
- /** Stores all previous cursor locations to display a trail. */
- private static LinkedList
- cursorX = new LinkedList(),
- cursorY = new LinkedList();
-
- /** Time to show volume image, in milliseconds. */
- private static final int VOLUME_DISPLAY_TIME = 1500;
-
- /** Volume display elapsed time. */
- private static int volumeDisplay = -1;
-
- /** The current bar notification string. */
- private static String barNotif;
-
- /** The current bar notification timer. */
- private static int barNotifTimer = -1;
-
- /** Duration, in milliseconds, to display bar notifications. */
- private static final int BAR_NOTIFICATION_TIME = 1250;
-
- /** The current tooltip. */
- private static String tooltip;
-
- /** Whether or not to check the current tooltip for line breaks. */
- private static boolean tooltipNewlines;
-
- /** The current tooltip timer. */
- private static int tooltipTimer = -1;
-
- /** Duration, in milliseconds, to fade tooltips. */
- private static final int TOOLTIP_FADE_TIME = 200;
-
- // game-related variables
- private static GameContainer container;
- private static StateBasedGame game;
- private static Input input;
-
- // This class should not be instantiated.
- private UI() {}
-
- /**
- * Initializes UI data.
- * @param container the game container
- * @param game the game object
- * @throws SlickException
- */
- public static void init(GameContainer container, StateBasedGame game)
- throws SlickException {
- UI.container = container;
- UI.game = game;
- UI.input = container.getInput();
-
- // hide native cursor
- try {
- int min = Cursor.getMinCursorSize();
- IntBuffer tmp = BufferUtils.createIntBuffer(min * min);
- emptyCursor = new Cursor(min, min, min/2, min/2, 1, tmp, null);
- hideCursor();
- } catch (LWJGLException e) {
- ErrorHandler.error("Failed to create hidden cursor.", e, true);
- }
-
- // back button
- if (GameImage.MENU_BACK.getImages() != null) {
- Animation back = GameImage.MENU_BACK.getAnimation(120);
- backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f));
- } else {
- Image back = GameImage.MENU_BACK.getImage();
- backButton = new MenuButton(back, back.getWidth() / 2f, container.getHeight() - (back.getHeight() / 2f));
- }
- backButton.setHoverExpand(MenuButton.Expand.UP_RIGHT);
- }
-
- /**
- * Updates all UI components by a delta interval.
- * @param delta the delta interval since the last call.
- */
- public static void update(int delta) {
- updateCursor(delta);
- updateVolumeDisplay(delta);
- updateBarNotification(delta);
- if (tooltipTimer > 0)
- tooltipTimer -= delta;
- }
-
- /**
- * Draws the global UI components: cursor, FPS, volume bar, bar notifications.
- * @param g the graphics context
- */
- public static void draw(Graphics g) {
- drawBarNotification(g);
- drawVolume(g);
- drawFPS();
- drawCursor();
- drawTooltip(g);
- }
-
- /**
- * Draws the global UI components: cursor, FPS, volume bar, bar notifications.
- * @param g the graphics context
- * @param mouseX the mouse x coordinate
- * @param mouseY the mouse y coordinate
- * @param mousePressed whether or not the mouse button is pressed
- */
- public static void draw(Graphics g, int mouseX, int mouseY, boolean mousePressed) {
- drawBarNotification(g);
- drawVolume(g);
- drawFPS();
- drawCursor(mouseX, mouseY, mousePressed);
- drawTooltip(g);
- }
-
- /**
- * Resets the necessary UI components upon entering a state.
- */
- public static void enter() {
- backButton.resetHover();
- resetBarNotification();
- resetCursorLocations();
- resetTooltip();
- }
-
- /**
- * Returns the 'menu-back' MenuButton.
- */
- public static MenuButton getBackButton() { return backButton; }
-
- /**
- * Draws a tab image and text centered at a location.
- * @param x the center x coordinate
- * @param y the center y coordinate
- * @param text the text to draw inside the tab
- * @param selected whether the tab is selected (white) or not (red)
- * @param isHover whether to include a hover effect (unselected only)
- */
- public static void drawTab(float x, float y, String text, boolean selected, boolean isHover) {
- Image tabImage = GameImage.MENU_TAB.getImage();
- float tabTextX = x - (Utils.FONT_MEDIUM.getWidth(text) / 2);
- float tabTextY = y - (tabImage.getHeight() / 2.5f);
- Color filter, textColor;
- if (selected) {
- filter = Color.white;
- textColor = Color.black;
- } else {
- filter = (isHover) ? Utils.COLOR_RED_HOVER : Color.red;
- textColor = Color.white;
- }
- tabImage.drawCentered(x, y, filter);
- Utils.FONT_MEDIUM.drawString(tabTextX, tabTextY, text, textColor);
- }
-
- /**
- * Draws the cursor.
- */
- public static void drawCursor() {
- int state = game.getCurrentStateID();
- boolean mousePressed =
- (((state == Opsu.STATE_GAME || state == Opsu.STATE_GAMEPAUSEMENU) && Utils.isGameKeyPressed()) ||
- ((input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) || input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) &&
- !(state == Opsu.STATE_GAME && Options.isMouseDisabled())));
- drawCursor(input.getMouseX(), input.getMouseY(), mousePressed);
- }
-
- /**
- * Draws the cursor.
- * @param mouseX the mouse x coordinate
- * @param mouseY the mouse y coordinate
- * @param mousePressed whether or not the mouse button is pressed
- */
- public static void drawCursor(int mouseX, int mouseY, boolean mousePressed) {
- // determine correct cursor image
- // TODO: most beatmaps don't skin CURSOR_MIDDLE, so how to determine style?
- Image cursor = null, cursorMiddle = null, cursorTrail = null;
- boolean skinned = GameImage.CURSOR.hasSkinImage();
- boolean newStyle = (skinned) ? true : Options.isNewCursorEnabled();
- if (skinned || newStyle) {
- cursor = GameImage.CURSOR.getImage();
- cursorTrail = GameImage.CURSOR_TRAIL.getImage();
- } else {
- cursor = GameImage.CURSOR_OLD.getImage();
- cursorTrail = GameImage.CURSOR_TRAIL_OLD.getImage();
- }
- if (newStyle)
- cursorMiddle = GameImage.CURSOR_MIDDLE.getImage();
-
- int removeCount = 0;
- int FPSmod = (Options.getTargetFPS() / 60);
-
- // TODO: use an image buffer
- /*
- if (newStyle) {
- // new style: add all points between cursor movements
- if (lastX < 0) {
- lastX = mouseX;
- lastY = mouseY;
- return;
- }
- addCursorPoints(lastX, lastY, mouseX, mouseY);
- lastX = mouseX;
- lastY = mouseY;
-
- removeCount = (cursorX.size() / (6 * FPSmod)) + 1;
- } else {
- // old style: sample one point at a time
- cursorX.add(mouseX);
- cursorY.add(mouseY);
-
- int max = 10 * FPSmod;
- if (cursorX.size() > max)
- removeCount = cursorX.size() - max;
- }*/
-
- // remove points from the lists
- for (int i = 0; i < removeCount && !cursorX.isEmpty(); i++) {
- cursorX.remove();
- cursorY.remove();
- }
-
- // draw a fading trail
- float alpha = 0f;
- float t = 2f / cursorX.size();
- Iterator iterX = cursorX.iterator();
- Iterator iterY = cursorY.iterator();
- while (iterX.hasNext()) {
- int cx = iterX.next();
- int cy = iterY.next();
- alpha += t;
- cursorTrail.setAlpha(alpha);
-// if (cx != x || cy != y)
- cursorTrail.drawCentered(cx, cy);
- }
- cursorTrail.drawCentered(mouseX, mouseY);
-
- // increase the cursor size if pressed
- final float scale = 1.25f;
- if (mousePressed) {
- cursor = cursor.getScaledCopy(scale);
- if (newStyle)
- cursorMiddle = cursorMiddle.getScaledCopy(scale);
- }
-
- // draw the other components
- if (newStyle)
- cursor.setRotation(cursorAngle);
- cursor.drawCentered(mouseX, mouseY);
- if (newStyle)
- cursorMiddle.drawCentered(mouseX, mouseY);
- }
-
- /**
- * Adds all points between (x1, y1) and (x2, y2) to the cursor point lists.
- * @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java
- */
- private static void addCursorPoints(int x1, int y1, int x2, int y2) {
- // delta of exact value and rounded value of the dependent variable
- int d = 0;
- int dy = Math.abs(y2 - y1);
- int dx = Math.abs(x2 - x1);
-
- int dy2 = (dy << 1); // slope scaling factors to avoid floating
- int dx2 = (dx << 1); // point
- int ix = x1 < x2 ? 1 : -1; // increment direction
- int iy = y1 < y2 ? 1 : -1;
-
- int k = 5; // sample size
- if (dy <= dx) {
- for (int i = 0; ; i++) {
- if (i == k) {
- cursorX.add(x1);
- cursorY.add(y1);
- i = 0;
- }
- if (x1 == x2)
- break;
- x1 += ix;
- d += dy2;
- if (d > dx) {
- y1 += iy;
- d -= dx2;
- }
- }
- } else {
- for (int i = 0; ; i++) {
- if (i == k) {
- cursorX.add(x1);
- cursorY.add(y1);
- i = 0;
- }
- if (y1 == y2)
- break;
- y1 += iy;
- d += dx2;
- if (d > dy) {
- x1 += ix;
- d -= dy2;
- }
- }
- }
- }
-
- /**
- * Rotates the cursor by a degree determined by a delta interval.
- * If the old style cursor is being used, this will do nothing.
- * @param delta the delta interval since the last call
- */
- private static void updateCursor(int delta) {
- cursorAngle += delta / 40f;
- cursorAngle %= 360;
- }
-
- /**
- * Resets all cursor data and skins.
- */
- public static void resetCursor() {
- GameImage.CURSOR.destroySkinImage();
- GameImage.CURSOR_MIDDLE.destroySkinImage();
- GameImage.CURSOR_TRAIL.destroySkinImage();
- cursorAngle = 0f;
- GameImage.CURSOR.getImage().setRotation(0f);
- }
-
- /**
- * Resets all cursor location data.
- */
- private static void resetCursorLocations() {
- lastX = lastY = -1;
- cursorX.clear();
- cursorY.clear();
- }
-
- /**
- * Hides the cursor, if possible.
- */
- public static void hideCursor() {
- if (emptyCursor != null) {
- try {
- container.setMouseCursor(emptyCursor, 0, 0);
- } catch (SlickException e) {
- ErrorHandler.error("Failed to hide the cursor.", e, true);
- }
- }
- }
-
- /**
- * Unhides the cursor.
- */
- public static void showCursor() {
- container.setDefaultMouseCursor();
- }
-
- /**
- * Draws the FPS at the bottom-right corner of the game container.
- * If the option is not activated, this will do nothing.
- */
- public static void drawFPS() {
- if (!Options.isFPSCounterEnabled())
- return;
-
- String fps = String.format("%dFPS", container.getFPS());
- Utils.FONT_BOLD.drawString(
- container.getWidth() * 0.997f - Utils.FONT_BOLD.getWidth(fps),
- container.getHeight() * 0.997f - Utils.FONT_BOLD.getHeight(fps),
- Integer.toString(container.getFPS()), Color.white
- );
- Utils.FONT_DEFAULT.drawString(
- container.getWidth() * 0.997f - Utils.FONT_BOLD.getWidth("FPS"),
- container.getHeight() * 0.997f - Utils.FONT_BOLD.getHeight("FPS"),
- "FPS", Color.white
- );
- }
-
- /**
- * Draws the volume bar on the middle right-hand side of the game container.
- * Only draws if the volume has recently been changed using with {@link #changeVolume(int)}.
- * @param g the graphics context
- */
- public static void drawVolume(Graphics g) {
- if (volumeDisplay == -1)
- return;
-
- int width = container.getWidth(), height = container.getHeight();
- Image img = GameImage.VOLUME.getImage();
-
- // move image in/out
- float xOffset = 0;
- float ratio = (float) volumeDisplay / VOLUME_DISPLAY_TIME;
- if (ratio <= 0.1f)
- xOffset = img.getWidth() * (1 - (ratio * 10f));
- else if (ratio >= 0.9f)
- xOffset = img.getWidth() * (1 - ((1 - ratio) * 10f));
-
- img.drawCentered(width - img.getWidth() / 2f + xOffset, height / 2f);
- float barHeight = img.getHeight() * 0.9f;
- float volume = Options.getMasterVolume();
- g.setColor(Color.white);
- g.fillRoundRect(
- width - (img.getWidth() * 0.368f) + xOffset,
- (height / 2f) - (img.getHeight() * 0.47f) + (barHeight * (1 - volume)),
- img.getWidth() * 0.15f, barHeight * volume, 3
- );
- }
-
- /**
- * Updates volume display by a delta interval.
- * @param delta the delta interval since the last call
- */
- private static void updateVolumeDisplay(int delta) {
- if (volumeDisplay == -1)
- return;
-
- volumeDisplay += delta;
- if (volumeDisplay > VOLUME_DISPLAY_TIME)
- volumeDisplay = -1;
- }
-
- /**
- * Changes the master volume by a unit (positive or negative).
- * @param units the number of units
- */
- public static void changeVolume(int units) {
- final float UNIT_OFFSET = 0.05f;
- Options.setMasterVolume(container, Utils.getBoundedValue(Options.getMasterVolume(), UNIT_OFFSET * units, 0f, 1f));
- if (volumeDisplay == -1)
- volumeDisplay = 0;
- else if (volumeDisplay >= VOLUME_DISPLAY_TIME / 10)
- volumeDisplay = VOLUME_DISPLAY_TIME / 10;
- }
-
- /**
- * Draws loading progress (OSZ unpacking, OsuFile parsing, sound loading)
- * at the bottom of the screen.
- */
- public static void drawLoadingProgress(Graphics g) {
- String text, file;
- int progress;
-
- // determine current action
- if ((file = OszUnpacker.getCurrentFileName()) != null) {
- text = "Unpacking new beatmaps...";
- progress = OszUnpacker.getUnpackerProgress();
- } else if ((file = OsuParser.getCurrentFileName()) != null) {
- text = (OsuParser.getStatus() == OsuParser.Status.INSERTING) ?
- "Updating database..." : "Loading beatmaps...";
- progress = OsuParser.getParserProgress();
- } else if ((file = SoundController.getCurrentFileName()) != null) {
- text = "Loading sounds...";
- progress = SoundController.getLoadingProgress();
- } else
- return;
-
- // draw loading info
- float marginX = container.getWidth() * 0.02f, marginY = container.getHeight() * 0.02f;
- float lineY = container.getHeight() - marginY;
- int lineOffsetY = Utils.FONT_MEDIUM.getLineHeight();
- if (Options.isLoadVerbose()) {
- // verbose: display percentages and file names
- Utils.FONT_MEDIUM.drawString(
- marginX, lineY - (lineOffsetY * 2),
- String.format("%s (%d%%)", text, progress), Color.white);
- Utils.FONT_MEDIUM.drawString(marginX, lineY - lineOffsetY, file, Color.white);
- } else {
- // draw loading bar
- Utils.FONT_MEDIUM.drawString(marginX, lineY - (lineOffsetY * 2), text, Color.white);
- g.setColor(Color.white);
- g.fillRoundRect(marginX, lineY - (lineOffsetY / 2f),
- (container.getWidth() - (marginX * 2f)) * progress / 100f, lineOffsetY / 4f, 4
- );
- }
- }
-
- /**
- * Draws a scroll bar.
- * @param g the graphics context
- * @param unitIndex the unit index
- * @param totalUnits the total number of units
- * @param maxShown the maximum number of units shown at one time
- * @param unitBaseX the base x coordinate of the units
- * @param unitBaseY the base y coordinate of the units
- * @param unitWidth the width of a unit
- * @param unitHeight the height of a unit
- * @param unitOffsetY the y offset between units
- * @param bgColor the scroll bar area background color (null if none)
- * @param scrollbarColor the scroll bar color
- * @param right whether or not to place the scroll bar on the right side of the unit
- */
- public static void drawScrollbar(
- Graphics g, int unitIndex, int totalUnits, int maxShown,
- float unitBaseX, float unitBaseY, float unitWidth, float unitHeight, float unitOffsetY,
- Color bgColor, Color scrollbarColor, boolean right
- ) {
- float scrollbarWidth = container.getWidth() * 0.00347f;
- float heightRatio = (float) (2.6701f * Math.exp(-0.81 * Math.log(totalUnits)));
- float scrollbarHeight = container.getHeight() * heightRatio;
- float scrollAreaHeight = unitHeight + unitOffsetY * (maxShown - 1);
- float offsetY = (scrollAreaHeight - scrollbarHeight) * ((float) unitIndex / (totalUnits - maxShown));
- float scrollbarX = unitBaseX + unitWidth - ((right) ? scrollbarWidth : 0);
- if (bgColor != null) {
- g.setColor(bgColor);
- g.fillRect(scrollbarX, unitBaseY, scrollbarWidth, scrollAreaHeight);
- }
- g.setColor(scrollbarColor);
- g.fillRect(scrollbarX, unitBaseY + offsetY, scrollbarWidth, scrollbarHeight);
- }
-
- /**
- * Sets or updates a tooltip for drawing.
- * Must be called with {@link #drawTooltip(Graphics)}.
- * @param delta the delta interval since the last call
- * @param s the tooltip text
- * @param newlines whether to check for line breaks ('\n')
- */
- public static void updateTooltip(int delta, String s, boolean newlines) {
- if (s != null) {
- tooltip = s;
- tooltipNewlines = newlines;
- if (tooltipTimer <= 0)
- tooltipTimer = delta;
- else
- tooltipTimer += delta * 2;
- if (tooltipTimer > TOOLTIP_FADE_TIME)
- tooltipTimer = TOOLTIP_FADE_TIME;
- }
- }
-
- /**
- * Draws a tooltip, if any, near the current mouse coordinates,
- * bounded by the container dimensions.
- * @param g the graphics context
- */
- public static void drawTooltip(Graphics g) {
- if (tooltipTimer <= 0 || tooltip == null)
- return;
-
- int containerWidth = container.getWidth(), containerHeight = container.getHeight();
- int margin = containerWidth / 100, textMarginX = 2;
- int offset = GameImage.CURSOR_MIDDLE.getImage().getWidth() / 2;
- int lineHeight = Utils.FONT_SMALL.getLineHeight();
- int textWidth = textMarginX * 2, textHeight = lineHeight;
- if (tooltipNewlines) {
- String[] lines = tooltip.split("\\n");
- int maxWidth = Utils.FONT_SMALL.getWidth(lines[0]);
- for (int i = 1; i < lines.length; i++) {
- int w = Utils.FONT_SMALL.getWidth(lines[i]);
- if (w > maxWidth)
- maxWidth = w;
- }
- textWidth += maxWidth;
- textHeight += lineHeight * (lines.length - 1);
- } else
- textWidth += Utils.FONT_SMALL.getWidth(tooltip);
-
- // get drawing coordinates
- int x = input.getMouseX() + offset, y = input.getMouseY() + offset;
- if (x + textWidth > containerWidth - margin)
- x = containerWidth - margin - textWidth;
- else if (x < margin)
- x = margin;
- if (y + textHeight > containerHeight - margin)
- y = containerHeight - margin - textHeight;
- else if (y < margin)
- y = margin;
-
- // draw tooltip text inside a filled rectangle
- float alpha = (float) tooltipTimer / TOOLTIP_FADE_TIME;
- float oldAlpha = Utils.COLOR_BLACK_ALPHA.a;
- Utils.COLOR_BLACK_ALPHA.a = alpha;
- g.setColor(Utils.COLOR_BLACK_ALPHA);
- Utils.COLOR_BLACK_ALPHA.a = oldAlpha;
- g.fillRect(x, y, textWidth, textHeight);
- oldAlpha = Utils.COLOR_DARK_GRAY.a;
- Utils.COLOR_DARK_GRAY.a = alpha;
- g.setColor(Utils.COLOR_DARK_GRAY);
- g.setLineWidth(1);
- g.drawRect(x, y, textWidth, textHeight);
- Utils.COLOR_DARK_GRAY.a = oldAlpha;
- oldAlpha = Utils.COLOR_WHITE_ALPHA.a;
- Utils.COLOR_WHITE_ALPHA.a = alpha;
- Utils.FONT_SMALL.drawString(x + textMarginX, y, tooltip, Utils.COLOR_WHITE_ALPHA);
- Utils.COLOR_WHITE_ALPHA.a = oldAlpha;
- }
-
- /**
- * Resets the tooltip.
- */
- public static void resetTooltip() {
- tooltipTimer = -1;
- tooltip = null;
- }
-
- /**
- * Submits a bar notification for drawing.
- * Must be called with {@link #drawBarNotification(Graphics)}.
- * @param s the notification string
- */
- public static void sendBarNotification(String s) {
- if (s != null) {
- barNotif = s;
- barNotifTimer = 0;
- }
- }
-
- /**
- * Updates the bar notification by a delta interval.
- * @param delta the delta interval since the last call
- */
- private static void updateBarNotification(int delta) {
- if (barNotifTimer > -1 && barNotifTimer < BAR_NOTIFICATION_TIME) {
- barNotifTimer += delta;
- if (barNotifTimer > BAR_NOTIFICATION_TIME)
- barNotifTimer = BAR_NOTIFICATION_TIME;
- }
- }
-
- /**
- * Resets the bar notification.
- */
- public static void resetBarNotification() {
- barNotifTimer = -1;
- barNotif = null;
- }
-
- /**
- * Draws the notification sent from {@link #sendBarNotification(String)}.
- * @param g the graphics context
- */
- public static void drawBarNotification(Graphics g) {
- if (barNotifTimer <= 0 || barNotifTimer >= BAR_NOTIFICATION_TIME)
- return;
-
- float alpha = 1f;
- if (barNotifTimer >= BAR_NOTIFICATION_TIME * 0.9f)
- alpha -= 1 - ((BAR_NOTIFICATION_TIME - barNotifTimer) / (BAR_NOTIFICATION_TIME * 0.1f));
- int midX = container.getWidth() / 2, midY = container.getHeight() / 2;
- float barHeight = Utils.FONT_LARGE.getLineHeight() * (1f + 0.6f * Math.min(barNotifTimer * 15f / BAR_NOTIFICATION_TIME, 1f));
- float oldAlphaB = Utils.COLOR_BLACK_ALPHA.a, oldAlphaW = Utils.COLOR_WHITE_ALPHA.a;
- Utils.COLOR_BLACK_ALPHA.a *= alpha;
- Utils.COLOR_WHITE_ALPHA.a = alpha;
- g.setColor(Utils.COLOR_BLACK_ALPHA);
- g.fillRect(0, midY - barHeight / 2f, container.getWidth(), barHeight);
- Utils.FONT_LARGE.drawString(
- midX - Utils.FONT_LARGE.getWidth(barNotif) / 2f,
- midY - Utils.FONT_LARGE.getLineHeight() / 2.2f,
- barNotif, Utils.COLOR_WHITE_ALPHA);
- Utils.COLOR_BLACK_ALPHA.a = oldAlphaB;
- Utils.COLOR_WHITE_ALPHA.a = oldAlphaW;
- }
-
- /**
- * Shows a confirmation dialog (used before exiting the game).
- * @param message the message to display
- * @return true if user selects "yes", false otherwise
- */
- public static boolean showExitConfirmation(String message) {
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- } catch (Exception e) {
- ErrorHandler.error("Could not set system look and feel for exit confirmation.", e, true);
- }
- int n = JOptionPane.showConfirmDialog(null, message, "Warning",
- JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
- return (n != JOptionPane.YES_OPTION);
- }
-}
diff --git a/src/itdelatrisu/opsu/beatmap/Beatmap.java b/src/itdelatrisu/opsu/beatmap/Beatmap.java
index ad8b5476..7216b0de 100644
--- a/src/itdelatrisu/opsu/beatmap/Beatmap.java
+++ b/src/itdelatrisu/opsu/beatmap/Beatmap.java
@@ -184,7 +184,10 @@ public class Beatmap implements Comparable {
/** Slider border color. If null, the skin value is used. */
public Color sliderBorder;
-
+
+ /** md5 hash of this file */
+ public String md5Hash;
+
/**
* [HitObjects]
*/
diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java
index 369ea19e..86d3dcb2 100644
--- a/src/itdelatrisu/opsu/beatmap/BeatmapParser.java
+++ b/src/itdelatrisu/opsu/beatmap/BeatmapParser.java
@@ -589,6 +589,8 @@ public class BeatmapParser {
// parse hit objects now?
if (parseObjects)
parseHitObjects(beatmap);
+
+ beatmap.md5Hash = Utils.getMD5(file);
return beatmap;
}
diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
index 7c5aab81..80362838 100644
--- a/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
+++ b/src/itdelatrisu/opsu/beatmap/BeatmapSetList.java
@@ -28,6 +28,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
@@ -57,6 +58,10 @@ public class BeatmapSetList {
/** Set of all beatmap set IDs for the parsed beatmaps. */
private HashSet MSIDdb;
+
+ /** Map of all hash to Beatmap . */
+ public HashMap beatmapHashesToFile;
+
/** Index of current expanded node (-1 if no node is expanded). */
private int expandedIndex;
@@ -83,6 +88,7 @@ public class BeatmapSetList {
private BeatmapSetList() {
parsedNodes = new ArrayList();
MSIDdb = new HashSet();
+ beatmapHashesToFile = new HashMap();
reset();
}
@@ -117,6 +123,10 @@ public class BeatmapSetList {
int msid = beatmaps.get(0).beatmapSetID;
if (msid > 0)
MSIDdb.add(msid);
+ for(Beatmap f : beatmaps) {
+ beatmapHashesToFile.put(f.md5Hash, f);
+ }
+
return node;
}
@@ -501,4 +511,8 @@ public class BeatmapSetList {
* @return true if id is in the list
*/
public boolean containsBeatmapSetID(int id) { return MSIDdb.contains(id); }
+
+ public Beatmap getFileFromBeatmapHash(String beatmapHash) {
+ return beatmapHashesToFile.get(beatmapHash);
+ }
}
\ No newline at end of file
diff --git a/src/itdelatrisu/opsu/db/BeatmapDB.java b/src/itdelatrisu/opsu/db/BeatmapDB.java
index 3f85373e..57508975 100644
--- a/src/itdelatrisu/opsu/db/BeatmapDB.java
+++ b/src/itdelatrisu/opsu/db/BeatmapDB.java
@@ -43,7 +43,7 @@ public class BeatmapDB {
* Current database version.
* This value should be changed whenever the database format changes.
*/
- private static final String DATABASE_VERSION = "2014-06-08";
+ private static final String DATABASE_VERSION = "2015-06-11";
/** Minimum batch size ratio ({@code batchSize/cacheSize}) to invoke batch loading. */
private static final float LOAD_BATCH_MIN_RATIO = 0.2f;
@@ -96,7 +96,7 @@ public class BeatmapDB {
insertStmt = connection.prepareStatement(
"INSERT INTO beatmaps VALUES (" +
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " +
- "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+ "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?");
deleteMapStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?");
@@ -122,7 +122,8 @@ public class BeatmapDB {
"bpmMin INTEGER, bpmMax INTEGER, endTime INTEGER, " +
"audioFile TEXT, audioLeadIn INTEGER, previewTime INTEGER, countdown INTEGER, sampleSet TEXT, stackLeniency REAL, " +
"mode INTEGER, letterboxInBreaks BOOLEAN, widescreenStoryboard BOOLEAN, epilepsyWarning BOOLEAN, " +
- "bg TEXT, sliderBorder TEXT, timingPoints TEXT, breaks TEXT, combo TEXT" +
+ "bg TEXT, sliderBorder TEXT, timingPoints TEXT, breaks TEXT, combo TEXT," +
+ "md5hash TEXT" +
"); " +
"CREATE TABLE IF NOT EXISTS info (" +
"key TEXT NOT NULL UNIQUE, value TEXT" +
@@ -340,6 +341,7 @@ public class BeatmapDB {
stmt.setString(38, beatmap.timingPointsToString());
stmt.setString(39, beatmap.breaksToString());
stmt.setString(40, beatmap.comboToString());
+ stmt.setString(41, beatmap.md5Hash);
} catch (SQLException e) {
throw e;
} catch (Exception e) {
@@ -481,6 +483,7 @@ public class BeatmapDB {
if (bg != null)
beatmap.bg = new File(dir, BeatmapParser.getDBString(bg));
beatmap.sliderBorderFromString(rs.getString(37));
+ beatmap.md5Hash = BeatmapParser.getDBString(rs.getString(41));
} catch (SQLException e) {
throw e;
} catch (Exception e) {
diff --git a/src/itdelatrisu/opsu/db/OsuDB.java b/src/itdelatrisu/opsu/db/OsuDB.java
deleted file mode 100644
index d632daa3..00000000
--- a/src/itdelatrisu/opsu/db/OsuDB.java
+++ /dev/null
@@ -1,584 +0,0 @@
-//todo rename
-
-/*
- * opsu! - an open-source osu! client
- * Copyright (C) 2014, 2015 Jeffrey Han
- *
- * opsu! is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * opsu! is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with opsu!. If not, see .
- */
-
-package itdelatrisu.opsu.db;
-
-import itdelatrisu.opsu.ErrorHandler;
-import itdelatrisu.opsu.Options;
-import itdelatrisu.opsu.OsuFile;
-import itdelatrisu.opsu.OsuParser;
-
-import java.io.File;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.newdawn.slick.util.Log;
-
-/**
- * Handles connections and queries with the cached beatmap database.
- */
-public class OsuDB {
- /**
- * Current database version.
- * This value should be changed whenever the database format changes.
- */
- private static final String DATABASE_VERSION = "2015-03-30";
-
- /** Minimum batch size ratio ({@code batchSize/cacheSize}) to invoke batch loading. */
- private static final float LOAD_BATCH_MIN_RATIO = 0.2f;
-
- /** Minimum batch size to invoke batch insertion. */
- private static final int INSERT_BATCH_MIN = 100;
-
- /** OsuFile loading flags. */
- public static final int LOAD_NONARRAY = 1, LOAD_ARRAY = 2, LOAD_ALL = 3;
-
- /** Database connection. */
- private static Connection connection;
-
- /** Query statements. */
- private static PreparedStatement insertStmt, selectStmt, deleteMapStmt, deleteGroupStmt, updateSizeStmt;
-
- /** Current size of beatmap cache table. */
- private static int cacheSize = -1;
-
- // This class should not be instantiated.
- private OsuDB() {}
-
- /**
- * Initializes the database connection.
- */
- public static void init() {
- // create a database connection
- connection = DBController.createConnection(Options.OSU_DB.getPath());
- if (connection == null)
- return;
-
- // create the database
- createDatabase();
-
- // prepare sql statements
- try {
- insertStmt = connection.prepareStatement(
- "INSERT INTO beatmaps VALUES (" +
- "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " +
- "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
- );
- selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?");
- deleteMapStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?");
- deleteGroupStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ?");
- updateSizeStmt = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('size', ?)");
- } catch (SQLException e) {
- ErrorHandler.error("Failed to prepare beatmap statements.", e, true);
- }
-
- // retrieve the cache size
- getCacheSize();
-
- // check the database version
- checkVersion();
- }
-
- /**
- * Creates the database, if it does not exist.
- */
- private static void createDatabase() {
- try (Statement stmt = connection.createStatement()) {
- String sql =
- "CREATE TABLE IF NOT EXISTS beatmaps (" +
- "dir TEXT, file TEXT, lastModified INTEGER, " +
- "MID INTEGER, MSID INTEGER, " +
- "title TEXT, titleUnicode TEXT, artist TEXT, artistUnicode TEXT, " +
- "creator TEXT, version TEXT, source TEXT, tags TEXT, " +
- "circles INTEGER, sliders INTEGER, spinners INTEGER, " +
- "hp REAL, cs REAL, od REAL, ar REAL, sliderMultiplier REAL, sliderTickRate REAL, " +
- "bpmMin INTEGER, bpmMax INTEGER, endTime INTEGER, " +
- "audioFile TEXT, audioLeadIn INTEGER, previewTime INTEGER, countdown INTEGER, sampleSet TEXT, stackLeniency REAL, " +
- "mode INTEGER, letterboxInBreaks BOOLEAN, widescreenStoryboard BOOLEAN, epilepsyWarning BOOLEAN, " +
- "bg TEXT, timingPoints TEXT, breaks TEXT, combo TEXT," +
- "md5hash TEXT" +
- "); " +
- "CREATE TABLE IF NOT EXISTS info (" +
- "key TEXT NOT NULL UNIQUE, value TEXT" +
- "); " +
- "CREATE INDEX IF NOT EXISTS idx ON beatmaps (dir, file); " +
-
- // extra optimizations
- "PRAGMA locking_mode = EXCLUSIVE; " +
- "PRAGMA journal_mode = WAL;";
- stmt.executeUpdate(sql);
-
- // set the version key, if empty
- sql = String.format("INSERT OR IGNORE INTO info(key, value) VALUES('version', '%s')", DATABASE_VERSION);
- stmt.executeUpdate(sql);
- } catch (SQLException e) {
- ErrorHandler.error("Could not create beatmap database.", e, true);
- }
- }
-
- /**
- * Checks the stored table version, clears the beatmap database if different
- * from the current version, then updates the version field.
- */
- private static void checkVersion() {
- try (Statement stmt = connection.createStatement()) {
- // get the stored version
- String sql = "SELECT value FROM info WHERE key = 'version'";
- ResultSet rs = stmt.executeQuery(sql);
- String version = (rs.next()) ? rs.getString(1) : "";
- rs.close();
-
- // if different from current version, clear the database
- if (!version.equals(DATABASE_VERSION)) {
- clearDatabase();
-
- // update version
- PreparedStatement ps = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('version', ?)");
- ps.setString(1, DATABASE_VERSION);
- ps.executeUpdate();
- ps.close();
- }
- } catch (SQLException e) {
- ErrorHandler.error("Beatmap database version checks failed.", e, true);
- }
- }
-
- /**
- * Retrieves the size of the beatmap cache from the 'info' table.
- */
- private static void getCacheSize() {
- try (Statement stmt = connection.createStatement()) {
- String sql = "SELECT value FROM info WHERE key = 'size'";
- ResultSet rs = stmt.executeQuery(sql);
- try {
- cacheSize = (rs.next()) ? Integer.parseInt(rs.getString(1)) : 0;
- } catch (NumberFormatException e) {
- cacheSize = 0;
- }
- rs.close();
- } catch (SQLException e) {
- ErrorHandler.error("Could not get beatmap cache size.", e, true);
- }
- }
-
- /**
- * Updates the size of the beatmap cache in the 'info' table.
- */
- private static void updateCacheSize() {
- if (connection == null)
- return;
-
- try {
- updateSizeStmt.setString(1, Integer.toString(Math.max(cacheSize, 0)));
- updateSizeStmt.executeUpdate();
- } catch (SQLException e) {
- ErrorHandler.error("Could not update beatmap cache size.", e, true);
- }
- }
-
- /**
- * Clears the database.
- */
- public static void clearDatabase() {
- if (connection == null)
- return;
-
- // drop the table, then recreate it
- try (Statement stmt = connection.createStatement()) {
- String sql = "DROP TABLE beatmaps";
- stmt.executeUpdate(sql);
- cacheSize = 0;
- updateCacheSize();
- } catch (SQLException e) {
- ErrorHandler.error("Could not drop beatmap database.", e, true);
- }
- createDatabase();
- }
-
- /**
- * Adds the OsuFile to the database.
- * @param osu the OsuFile object
- */
- public static void insert(OsuFile osu) {
- if (connection == null)
- return;
-
- try {
- setStatementFields(insertStmt, osu);
- cacheSize += insertStmt.executeUpdate();
- updateCacheSize();
- } catch (SQLException e) {
- ErrorHandler.error("Failed to add beatmap to database.", e, true);
- }
- }
-
- /**
- * Adds the OsuFiles to the database in a batch.
- * @param batch a list of OsuFile objects
- */
- public static void insert(List batch) {
- if (connection == null)
- return;
-
- try (Statement stmt = connection.createStatement()) {
- // turn off auto-commit mode
- boolean autoCommit = connection.getAutoCommit();
- connection.setAutoCommit(false);
-
- // drop indexes
- boolean recreateIndexes = (batch.size() >= INSERT_BATCH_MIN);
- if (recreateIndexes) {
- String sql = "DROP INDEX IF EXISTS idx";
- stmt.executeUpdate(sql);
- }
-
- // batch insert
- for (OsuFile osu : batch) {
- try {
- setStatementFields(insertStmt, osu);
- } catch (SQLException e) {
- Log.error(String.format("Failed to insert map '%s' into database.", osu.getFile().getPath()), e);
- continue;
- }
- insertStmt.addBatch();
- }
- int[] results = insertStmt.executeBatch();
- for (int i = 0; i < results.length; i++) {
- if (results[i] > 0)
- cacheSize += results[i];
- }
-
- // re-create indexes
- if (recreateIndexes) {
- String sql = "CREATE INDEX idx ON beatmaps (dir, file)";
- stmt.executeUpdate(sql);
- }
-
- // restore previous auto-commit mode
- connection.commit();
- connection.setAutoCommit(autoCommit);
-
- // update cache size
- updateCacheSize();
- } catch (SQLException e) {
- ErrorHandler.error("Failed to add beatmaps to database.", e, true);
- }
- }
-
- /**
- * Sets all statement fields using a given OsuFile object.
- * @param stmt the statement to set fields for
- * @param osu the OsuFile
- * @throws SQLException
- */
- private static void setStatementFields(PreparedStatement stmt, OsuFile osu)
- throws SQLException {
- try {
- stmt.setString(1, osu.getFile().getParentFile().getName());
- stmt.setString(2, osu.getFile().getName());
- stmt.setLong(3, osu.getFile().lastModified());
- stmt.setInt(4, osu.beatmapID);
- stmt.setInt(5, osu.beatmapSetID);
- stmt.setString(6, osu.title);
- stmt.setString(7, osu.titleUnicode);
- stmt.setString(8, osu.artist);
- stmt.setString(9, osu.artistUnicode);
- stmt.setString(10, osu.creator);
- stmt.setString(11, osu.version);
- stmt.setString(12, osu.source);
- stmt.setString(13, osu.tags);
- stmt.setInt(14, osu.hitObjectCircle);
- stmt.setInt(15, osu.hitObjectSlider);
- stmt.setInt(16, osu.hitObjectSpinner);
- stmt.setFloat(17, osu.HPDrainRate);
- stmt.setFloat(18, osu.circleSize);
- stmt.setFloat(19, osu.overallDifficulty);
- stmt.setFloat(20, osu.approachRate);
- stmt.setFloat(21, osu.sliderMultiplier);
- stmt.setFloat(22, osu.sliderTickRate);
- stmt.setInt(23, osu.bpmMin);
- stmt.setInt(24, osu.bpmMax);
- stmt.setInt(25, osu.endTime);
- stmt.setString(26, osu.audioFilename.getName());
- stmt.setInt(27, osu.audioLeadIn);
- stmt.setInt(28, osu.previewTime);
- stmt.setByte(29, osu.countdown);
- stmt.setString(30, osu.sampleSet);
- stmt.setFloat(31, osu.stackLeniency);
- stmt.setByte(32, osu.mode);
- stmt.setBoolean(33, osu.letterboxInBreaks);
- stmt.setBoolean(34, osu.widescreenStoryboard);
- stmt.setBoolean(35, osu.epilepsyWarning);
- stmt.setString(36, osu.bg);
- stmt.setString(37, osu.timingPointsToString());
- stmt.setString(38, osu.breaksToString());
- stmt.setString(39, osu.comboToString());
- stmt.setString(40, osu.md5Hash);
- } catch (SQLException e) {
- throw e;
- } catch (Exception e) {
- throw new SQLException(e);
- }
- }
-
- /**
- * Loads OsuFile fields from the database.
- * @param osu the OsuFile object
- * @param flag whether to load all fields (LOAD_ALL), non-array
- * fields (LOAD_NONARRAY), or array fields (LOAD_ARRAY)
- */
- public static void load(OsuFile osu, int flag) {
- if (connection == null)
- return;
-
- try {
- selectStmt.setString(1, osu.getFile().getParentFile().getName());
- selectStmt.setString(2, osu.getFile().getName());
- ResultSet rs = selectStmt.executeQuery();
- if (rs.next()) {
- if ((flag & LOAD_NONARRAY) > 0)
- setOsuFileFields(rs, osu);
- if ((flag & LOAD_ARRAY) > 0)
- setOsuFileArrayFields(rs, osu);
- }
- rs.close();
- } catch (SQLException e) {
- ErrorHandler.error("Failed to load OsuFile from database.", e, true);
- }
- }
-
- /**
- * Loads OsuFile fields from the database in a batch.
- * @param batch a list of OsuFile objects
- * @param flag whether to load all fields (LOAD_ALL), non-array
- * fields (LOAD_NONARRAY), or array fields (LOAD_ARRAY)
- */
- public static void load(List batch, int flag) {
- if (connection == null)
- return;
-
- // batch size too small
- int size = batch.size();
- if (size < cacheSize * LOAD_BATCH_MIN_RATIO) {
- for (OsuFile osu : batch)
- load(osu, flag);
- return;
- }
-
- try (Statement stmt = connection.createStatement()) {
- // create map
- HashMap> map = new HashMap>();
- for (OsuFile osu : batch) {
- String parent = osu.getFile().getParentFile().getName();
- String name = osu.getFile().getName();
- HashMap m = map.get(parent);
- if (m == null) {
- m = new HashMap();
- map.put(parent, m);
- }
- m.put(name, osu);
- }
-
- // iterate through database to load OsuFiles
- int count = 0;
- stmt.setFetchSize(100);
- String sql = "SELECT * FROM beatmaps";
- ResultSet rs = stmt.executeQuery(sql);
- while (rs.next()) {
- String parent = rs.getString(1);
- HashMap m = map.get(parent);
- if (m != null) {
- String name = rs.getString(2);
- OsuFile osu = m.get(name);
- if (osu != null) {
- try {
- if ((flag & LOAD_NONARRAY) > 0)
- setOsuFileFields(rs, osu);
- if ((flag & LOAD_ARRAY) > 0)
- setOsuFileArrayFields(rs, osu);
- } catch (SQLException e) {
- Log.error(String.format("Failed to load map '%s/%s' from database.", parent, name), e);
- }
- if (++count >= size)
- break;
- }
- }
- }
- rs.close();
- } catch (SQLException e) {
- ErrorHandler.error("Failed to load OsuFiles from database.", e, true);
- }
- }
-
- /**
- * Sets all OsuFile non-array fields using a given result set.
- * @param rs the result set containing the fields
- * @param osu the OsuFile
- * @throws SQLException
- */
- private static void setOsuFileFields(ResultSet rs, OsuFile osu) throws SQLException {
- try {
- osu.beatmapID = rs.getInt(4);
- osu.beatmapSetID = rs.getInt(5);
- osu.title = OsuParser.getDBString(rs.getString(6));
- osu.titleUnicode = OsuParser.getDBString(rs.getString(7));
- osu.artist = OsuParser.getDBString(rs.getString(8));
- osu.artistUnicode = OsuParser.getDBString(rs.getString(9));
- osu.creator = OsuParser.getDBString(rs.getString(10));
- osu.version = OsuParser.getDBString(rs.getString(11));
- osu.source = OsuParser.getDBString(rs.getString(12));
- osu.tags = OsuParser.getDBString(rs.getString(13));
- osu.hitObjectCircle = rs.getInt(14);
- osu.hitObjectSlider = rs.getInt(15);
- osu.hitObjectSpinner = rs.getInt(16);
- osu.HPDrainRate = rs.getFloat(17);
- osu.circleSize = rs.getFloat(18);
- osu.overallDifficulty = rs.getFloat(19);
- osu.approachRate = rs.getFloat(20);
- osu.sliderMultiplier = rs.getFloat(21);
- osu.sliderTickRate = rs.getFloat(22);
- osu.bpmMin = rs.getInt(23);
- osu.bpmMax = rs.getInt(24);
- osu.endTime = rs.getInt(25);
- osu.audioFilename = new File(osu.getFile().getParentFile(), OsuParser.getDBString(rs.getString(26)));
- osu.audioLeadIn = rs.getInt(27);
- osu.previewTime = rs.getInt(28);
- osu.countdown = rs.getByte(29);
- osu.sampleSet = OsuParser.getDBString(rs.getString(30));
- osu.stackLeniency = rs.getFloat(31);
- osu.mode = rs.getByte(32);
- osu.letterboxInBreaks = rs.getBoolean(33);
- osu.widescreenStoryboard = rs.getBoolean(34);
- osu.epilepsyWarning = rs.getBoolean(35);
- osu.bg = OsuParser.getDBString(rs.getString(36));
- osu.md5Hash = OsuParser.getDBString(rs.getString(40));
- } catch (SQLException e) {
- throw e;
- } catch (Exception e) {
- throw new SQLException(e);
- }
- }
-
- /**
- * Sets all OsuFile array fields using a given result set.
- * @param rs the result set containing the fields
- * @param osu the OsuFile
- * @throws SQLException
- */
- private static void setOsuFileArrayFields(ResultSet rs, OsuFile osu) throws SQLException {
- try {
- osu.timingPointsFromString(rs.getString(37));
- osu.breaksFromString(rs.getString(38));
- osu.comboFromString(rs.getString(39));
- } catch (SQLException e) {
- throw e;
- } catch (Exception e) {
- throw new SQLException(e);
- }
- }
-
- /**
- * Returns a map of file paths ({dir}/{file}) to last modified times, or
- * null if any error occurred.
- */
- public static Map getLastModifiedMap() {
- if (connection == null)
- return null;
-
- try (Statement stmt = connection.createStatement()) {
- Map map = new HashMap();
- String sql = "SELECT dir, file, lastModified FROM beatmaps";
- ResultSet rs = stmt.executeQuery(sql);
- stmt.setFetchSize(100);
- while (rs.next()) {
- String path = String.format("%s/%s", rs.getString(1), rs.getString(2));
- long lastModified = rs.getLong(3);
- map.put(path, lastModified);
- }
- rs.close();
- return map;
- } catch (SQLException e) {
- ErrorHandler.error("Failed to get last modified map from database.", e, true);
- return null;
- }
- }
-
- /**
- * Deletes the beatmap entry from the database.
- * @param dir the directory
- * @param file the file
- */
- public static void delete(String dir, String file) {
- if (connection == null)
- return;
-
- try {
- deleteMapStmt.setString(1, dir);
- deleteMapStmt.setString(2, file);
- cacheSize -= deleteMapStmt.executeUpdate();
- updateCacheSize();
- } catch (SQLException e) {
- ErrorHandler.error("Failed to delete beatmap entry from database.", e, true);
- }
- }
-
- /**
- * Deletes the beatmap group entry from the database.
- * @param dir the directory
- */
- public static void delete(String dir) {
- if (connection == null)
- return;
-
- try {
- deleteGroupStmt.setString(1, dir);
- cacheSize -= deleteGroupStmt.executeUpdate();
- updateCacheSize();
- } catch (SQLException e) {
- ErrorHandler.error("Failed to delete beatmap group entry from database.", e, true);
- }
- }
-
- /**
- * Closes the connection to the database.
- */
- public static void closeConnection() {
- if (connection == null)
- return;
-
- try {
- insertStmt.close();
- selectStmt.close();
- deleteMapStmt.close();
- deleteGroupStmt.close();
- updateSizeStmt.close();
- connection.close();
- connection = null;
- } catch (SQLException e) {
- ErrorHandler.error("Failed to close beatmap database.", e, true);
- }
- }
-}
diff --git a/src/itdelatrisu/opsu/objects/Circle.java b/src/itdelatrisu/opsu/objects/Circle.java
index cb9bada1..7d3637d9 100644
--- a/src/itdelatrisu/opsu/objects/Circle.java
+++ b/src/itdelatrisu/opsu/objects/Circle.java
@@ -66,7 +66,7 @@ public class Circle implements GameObject {
*/
public static void init(GameContainer container, float circleSize) {
diameter = (108 - (circleSize * 8));
- diameter = (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480)
+ diameter = (diameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
int diameterInt = (int)diameter;
GameImage.HITCIRCLE.setImage(GameImage.HITCIRCLE.getImage().getScaledCopy(diameterInt, diameterInt));
GameImage.HITCIRCLE_OVERLAY.setImage(GameImage.HITCIRCLE_OVERLAY.getImage().getScaledCopy(diameterInt, diameterInt));
diff --git a/src/itdelatrisu/opsu/objects/GameObject.java b/src/itdelatrisu/opsu/objects/GameObject.java
index ca1ab8e5..5ce189c4 100644
--- a/src/itdelatrisu/opsu/objects/GameObject.java
+++ b/src/itdelatrisu/opsu/objects/GameObject.java
@@ -69,4 +69,9 @@ public interface GameObject {
* Updates the position of the hit object.
*/
public void updatePosition();
+
+ /**
+ * Resets the hit object so that it can be reused.
+ */
+ public void reset();
}
diff --git a/src/itdelatrisu/opsu/objects/HitObject.java b/src/itdelatrisu/opsu/objects/HitObject.java
deleted file mode 100644
index d4602e2d..00000000
--- a/src/itdelatrisu/opsu/objects/HitObject.java
+++ /dev/null
@@ -1,79 +0,0 @@
-//TODO rename
-
-/*
- * opsu! - an open-source osu! client
- * Copyright (C) 2014, 2015 Jeffrey Han
- *
- * opsu! is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * opsu! is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with opsu!. If not, see .
- */
-
-package itdelatrisu.opsu.objects;
-
-import org.newdawn.slick.Graphics;
-
-/**
- * Hit object interface.
- */
-public interface HitObject {
- /**
- * Draws the hit object to the graphics context.
- * @param g the graphics context
- * @param trackPosition the current track position
- */
- public void draw(Graphics g, int trackPosition);
-
- /**
- * Updates the hit object.
- * @param overlap true if the next object's start time has already passed
- * @param delta the delta interval since the last call
- * @param mouseX the x coordinate of the mouse
- * @param mouseY the y coordinate of the mouse
- * @param keyPressed whether or not a game key is currently pressed
- * @param trackPosition the track position
- * @return true if object ended
- */
- public boolean update(boolean overlap, int delta, int mouseX, int mouseY, boolean keyPressed, int trackPosition);
-
- /**
- * Processes a mouse click.
- * @param x the x coordinate of the mouse
- * @param y the y coordinate of the mouse
- * @param trackPosition the track position
- * @return true if a hit result was processed
- */
- public boolean mousePressed(int x, int y, int trackPosition);
-
- /**
- * Returns the coordinates of the hit object at a given track position.
- * @param trackPosition the track position
- * @return the [x,y] coordinates
- */
- public float[] getPointAt(int trackPosition);
-
- /**
- * Returns the end time of the hit object.
- * @return the end time, in milliseconds
- */
- public int getEndTime();
-
- /**
- * Updates the position of the hit object.
- */
- public void updatePosition();
-
- /**
- * Resets the hit object so that it can be reused.
- */
- public void reset();
-}
diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java
index ac50e429..c70b4377 100644
--- a/src/itdelatrisu/opsu/objects/Slider.java
+++ b/src/itdelatrisu/opsu/objects/Slider.java
@@ -118,7 +118,7 @@ public class Slider implements GameObject {
containerHeight = container.getHeight();
diameter = (108 - (circleSize * 8));
- diameter = (diameter * OsuHitObject.getXMultiplier()); // convert from Osupixels (640x480)
+ diameter = (diameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
int diameterInt = (int)diameter;
followRadius = diameter / 2 * 3f;
diff --git a/src/itdelatrisu/opsu/replay/Replay.java b/src/itdelatrisu/opsu/replay/Replay.java
index 2e34cd12..712e920f 100644
--- a/src/itdelatrisu/opsu/replay/Replay.java
+++ b/src/itdelatrisu/opsu/replay/Replay.java
@@ -21,9 +21,9 @@ package itdelatrisu.opsu.replay;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameMod;
import itdelatrisu.opsu.Options;
-import itdelatrisu.opsu.OsuFile;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.Utils;
+import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.io.OsuReader;
import itdelatrisu.opsu.io.OsuWriter;
@@ -150,7 +150,7 @@ public class Replay {
* @param osu the OsuFile
* @return the ScoreData object
*/
- public ScoreData getScoreData(OsuFile osu) {
+ public ScoreData getScoreData(Beatmap osu) {
if (scoreData != null)
return scoreData;
@@ -186,7 +186,6 @@ public class Replay {
private void loadHeader(OsuReader reader) throws IOException {
this.mode = reader.readByte();
this.version = reader.readInt();
- //System.out.println("Header:"+file.getName()+" "+mode+" "+version);
this.beatmapHash = reader.readString();
this.playerName = reader.readString();
this.replayHash = reader.readString();
@@ -208,7 +207,6 @@ public class Replay {
* @throws IOException
*/
private void loadData(OsuReader reader) throws IOException {
- //System.out.println("Load Data");
// life data
String[] lifeData = reader.readString().split(",");
List lifeFrameList = new ArrayList(lifeData.length);
diff --git a/src/itdelatrisu/opsu/replay/ReplayImporter.java b/src/itdelatrisu/opsu/replay/ReplayImporter.java
index 1893f32c..51437aa4 100644
--- a/src/itdelatrisu/opsu/replay/ReplayImporter.java
+++ b/src/itdelatrisu/opsu/replay/ReplayImporter.java
@@ -2,9 +2,9 @@ package itdelatrisu.opsu.replay;
import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options;
-import itdelatrisu.opsu.OsuFile;
-import itdelatrisu.opsu.OsuGroupList;
import itdelatrisu.opsu.ScoreData;
+import itdelatrisu.opsu.beatmap.Beatmap;
+import itdelatrisu.opsu.beatmap.BeatmapSetList;
import itdelatrisu.opsu.db.ScoreDB;
import java.io.File;
@@ -16,7 +16,7 @@ public class ReplayImporter {
try {
Replay r = new Replay(replayToImport);
r.loadHeader();
- OsuFile oFile = OsuGroupList.get().getFileFromBeatmapHash(r.beatmapHash);
+ Beatmap oFile = BeatmapSetList.get().getFileFromBeatmapHash(r.beatmapHash);
if(oFile != null){
File replaydir = Options.getReplayDir();
if (!replaydir.isDirectory()) {
diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java
index acb371c4..0b80a774 100644
--- a/src/itdelatrisu/opsu/states/Game.java
+++ b/src/itdelatrisu/opsu/states/Game.java
@@ -618,21 +618,21 @@ public class Game extends BasicGameState {
if(replayIndex-1 >= 1 && replayIndex < replay.frames.length && trackPosition < replay.frames[replayIndex-1].getTime()){
replayIndex = 0;
while(objectIndex>=0){
- hitObjects[objectIndex].reset();
+ gameObjects[objectIndex].reset();
objectIndex--;
}
- // load the first timingPoint
- if (!osu.timingPoints.isEmpty()) {
- OsuTimingPoint timingPoint = osu.timingPoints.get(0);
+ // reset game data
+ resetGameData();
+
+ // load the first timingPoint for stacking
+ if (!beatmap.timingPoints.isEmpty()) {
+ TimingPoint timingPoint = beatmap.timingPoints.get(0);
if (!timingPoint.isInherited()) {
- beatLengthBase = beatLength = timingPoint.getBeatLength();
- HitSound.setDefaultSampleSet(timingPoint.getSampleType());
- SoundController.setSampleVolume(timingPoint.getSampleVolume());
+ setBeatLength(timingPoint, true);
timingPointIndex++;
}
}
- resetGameData();
}
// update and run replay frames
@@ -777,8 +777,8 @@ public class Game extends BasicGameState {
boolean keyPressed = keys != ReplayFrame.KEY_NONE;
while (objectIndex < gameObjects.length && trackPosition > beatmap.objects[objectIndex].getTime()) {
// check if we've already passed the next object's start time
- boolean overlap = (objectIndex + 1 < hitObjects.length &&
- trackPosition > osu.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_50]);
+ boolean overlap = (objectIndex + 1 < gameObjects.length &&
+ trackPosition > beatmap.objects[objectIndex + 1].getTime() - hitResultOffset[GameData.HIT_50]);
// update hit object and check completion status
if (gameObjects[objectIndex].update(overlap, delta, mouseX, mouseY, keyPressed, trackPosition))
@@ -915,8 +915,15 @@ public class Game extends BasicGameState {
// skip button
if (skipButton.contains(x, y))
skipIntro();
+
+ // playback speed button
+ else if (playbackSpeed.getButton().contains(x, y)) {
+ playbackSpeed = playbackSpeed.next();
+ MusicController.setPitch(GameMod.getSpeedMultiplier() * playbackSpeed.getModifier());
+ }
+
if(y < 50){
- float pos = (float)x / width * osu.endTime;
+ float pos = (float)x / width * beatmap.endTime;
System.out.println("Seek to"+pos);
MusicController.setPosition((int)pos);
}
@@ -1067,6 +1074,7 @@ public class Game extends BasicGameState {
} else if (restart == Restart.REPLAY)
retries = 0;
+ gameObjects = new GameObject[beatmap.objects.length];
// reset game data
resetGameData();
@@ -1078,14 +1086,6 @@ public class Game extends BasicGameState {
timingPointIndex++;
}
}
-
- if (!osu.timingPoints.isEmpty()) {
- OsuTimingPoint timingPoint = osu.timingPoints.get(0);
- if (!timingPoint.isInherited()) {
- beatLengthBase = beatLength = timingPoint.getBeatLength();
- }
- }
- hitObjects = new HitObject[osu.objects.length];
// initialize object maps
Color[] combo = beatmap.getComboColors();
@@ -1094,7 +1094,7 @@ public class Game extends BasicGameState {
// is this the last note in the combo?
boolean comboEnd = false;
- if (i + 1 >= osu.objects.length || osu.objects[i + 1].isNewCombo())
+ if (i + 1 < beatmap.objects.length && beatmap.objects[i + 1].isNewCombo())
comboEnd = true;
Color color = combo[hitObject.getComboIndex()];
@@ -1145,7 +1145,6 @@ public class Game extends BasicGameState {
// load replay frames
if (isReplay) {
- //System.out.println(replay.toString());
// load mods
previousMods = GameMod.getModState();
GameMod.loadModState(replay.mods);
@@ -1310,9 +1309,6 @@ public class Game extends BasicGameState {
* Resets all game data and structures.
*/
public void resetGameData() {
- //conflict
- gameObjects = new GameObject[beatmap.objects.length];
- //
data.clear();
objectIndex = 0;
breakIndex = 0;