taiko-web/public/src/js/parseosu.js

377 lines
9.1 KiB
JavaScript
Raw Normal View History

2018-10-11 00:13:24 +02:00
class ParseOsu{
constructor(fileContent, offset){
2018-09-15 16:34:53 +02:00
this.osu = {
OFFSET: 0,
MSPERBEAT: 1,
METER: 2,
SAMPLESET: 3,
SAMPLEINDEX: 4,
VOLUME: 5,
INHERITED: 6,
KIAIMODE: 7,
X: 0,
Y: 1,
TIME: 2,
TYPE: 3,
HITSOUND: 4,
EXTRAS: 5,
ENDTIME: 5,
CIRCLE: 1,
SLIDER: 2,
NEWCOMBO: 4,
SPINNER: 8,
NORMAL: 1,
WHISTLE: 2,
FINISH: 4,
CLAP: 8,
CURVEPOINTS: 0,
REPEAT: 1,
PIXELLENGTH: 2,
EDGEHITSOUNDS: 3,
EDGEADDITIONS: 4
}
2018-09-27 20:39:25 +02:00
this.data = []
for(let line of fileContent){
2018-10-11 00:13:24 +02:00
line = line.replace(/\/\/.*/, "").trim()
2018-09-27 20:39:25 +02:00
if(line !== ""){
this.data.push(line)
}
}
2018-10-11 00:13:24 +02:00
this.offset = (offset || 0) * -1000
this.soundOffset = 0
2018-09-15 16:34:53 +02:00
this.beatInfo = {
beatInterval: 0,
2018-09-27 20:39:25 +02:00
lastBeatInterval: 0,
2018-09-15 16:34:53 +02:00
bpm: 0
}
this.generalInfo = this.parseGeneralInfo()
this.metadata = this.parseMetadata()
this.editor = this.parseEditor()
this.difficulty = this.parseDifficulty()
this.timingPoints = this.parseTiming()
this.circles = this.parseCircles()
this.measures = this.parseMeasures()
}
getStartEndIndexes(type){
var indexes = {
start: 0,
end: 0
}
while(indexes.start < this.data.length){
2018-09-27 20:39:25 +02:00
if(this.data[indexes.start] === "[" + type + "]"){
2018-09-15 16:34:53 +02:00
break
}
indexes.start++
}
indexes.start++
indexes.end = indexes.start
while(indexes.end < this.data.length){
2018-09-27 20:39:25 +02:00
if(this.data[indexes.end].match(/^\[\w+\]$/)){
2018-09-15 16:34:53 +02:00
break
}
indexes.end++
}
indexes.end--
return indexes
}
parseDifficulty(){
var difficulty = {
sliderMultiplier: 0,
sliderTickRate: 0,
approachRate: 0
}
var indexes = this.getStartEndIndexes("Difficulty")
for(var i = indexes.start; i <= indexes.end; i++){
var [item, key] = this.data[i].split(":")
switch(item){
case "SliderMultiplier":
difficulty.sliderMultiplier = key
difficulty.originalMultiplier = key
break
case "SliderTickRate":
difficulty.sliderTickRate = key
break
case "ApproachRate":
difficulty.approachRate = key
break
case "OverallDifficulty":
difficulty.overallDifficulty = key
break
}
}
return difficulty
}
parseTiming(){
var timingPoints = []
var indexes = this.getStartEndIndexes("TimingPoints")
var lastBeatInterval = parseInt(this.data[indexes.start].split(",")[1])
for(var i = indexes.start; i <= indexes.end; i++){
2018-09-15 16:34:53 +02:00
var values = this.data[i].split(",")
var start = parseInt(values[this.osu.OFFSET])
var msOrPercent = parseFloat(values[this.osu.MSPERBEAT])
if(i == indexes.start){
this.beatInfo.beatInterval = msOrPercent
this.beatInfo.bpm = Math.floor(1000 / this.beatInfo.beatInterval * 60)
}
var beatReset = false
2018-09-15 16:34:53 +02:00
if(msOrPercent < 0){
var sliderMultiplier = this.difficulty.lastMultiplier / Math.abs(msOrPercent / 100)
}else{
var sliderMultiplier = 1000 / msOrPercent
2018-09-15 16:34:53 +02:00
if(i == 0){
this.difficulty.originalMultiplier = sliderMultiplier
}
this.difficulty.lastMultiplier = sliderMultiplier
beatReset = true
2018-09-15 16:34:53 +02:00
}
timingPoints.push({
2018-10-11 00:13:24 +02:00
start: start + this.offset,
2018-09-15 16:34:53 +02:00
sliderMultiplier: sliderMultiplier,
2018-09-20 01:20:26 +02:00
measure: parseInt(values[this.osu.METER]),
gogoTime: parseInt(values[this.osu.KIAIMODE]),
beatMS: 1000 / this.difficulty.lastMultiplier,
beatReset: beatReset
2018-09-15 16:34:53 +02:00
})
}
return timingPoints
}
parseMeasures(){
var measures = []
for(var i = 0; i < this.timingPoints.length; i++){
var currentTiming = this.timingPoints[i]
var firstTiming = i === 0
var limit = this.circles[this.circles.length - 1].endTime + currentTiming.beatMS
for(var j = i + 1; j < this.timingPoints.length; j++){
var nextTiming = this.timingPoints[j]
var newLimit = nextTiming.start
if(nextTiming.measure !== currentTiming.measure || nextTiming.beatReset){
limit = newLimit - currentTiming.beatMS
break
}
i = j
}
var start = currentTiming.start
var interval = currentTiming.beatMS * currentTiming.measure
if(firstTiming){
while(start >= interval){
start -= interval
}
2018-09-15 16:34:53 +02:00
}
for(var ms = start; ms <= limit; ms += interval){
var speed = currentTiming.sliderMultiplier
for(var j = 0; j < this.timingPoints.length; j++){
var timingPoint = this.timingPoints[j]
if(j !== 0 && timingPoint.start - this.offset > ms){
break
}
speed = timingPoint.sliderMultiplier
2015-07-17 10:22:46 +02:00
}
measures.push({
ms: ms,
originalMS: ms,
speed: speed
})
2018-09-15 16:34:53 +02:00
}
}
return measures
}
parseGeneralInfo(){
var generalInfo = {
audioFilename: "",
audioWait: 0
}
var indexes = this.getStartEndIndexes("General")
for(var i = indexes.start; i<= indexes.end; i++){
var [item, key] = this.data[i].split(":")
switch(item){
case "SliderMultiple":
generalInfo.audioFilename = key
break
case "AudioWait":
generalInfo.audioWait = parseInt(key)
break
}
}
return generalInfo
}
parseMetadata(){
var metadata = {
title: "",
artist: ""
}
var indexes = this.getStartEndIndexes("Metadata")
for(var i = indexes.start; i <= indexes.end; i++){
var [item, key] = this.data[i].split(":")
switch(item){
case "TitleUnicode":
metadata.title = key
break
case "ArtistUnicode":
metadata.artist = key
break
}
}
return metadata
}
parseEditor(){
var editor = {
distanceSpacing: 0,
beatDivisor: 0,
gridSize: 0
}
var indexes = this.getStartEndIndexes("Editor")
for(var i = indexes.start; i <= indexes.end; i++){
var [item, key] = this.data[i].split(":")
switch(item){
case "DistanceSpacing":
editor.distanceSpacing = parseFloat(key)
break
case "BeatDivisor":
editor.beatDivisor = parseInt(key)
break
case "GridSize":
editor.gridSize = parseInt(key)
break
}
}
return editor
}
difficultyRange(difficulty, min, mid, max){
if(difficulty > 5){
return mid + (max - mid) * (difficulty - 5) / 5
}
if(difficulty < 5){
return mid - (mid - min) * (5 - difficulty) / 5
}
return mid
}
parseCircles(){
var circles = []
var circleID = 0
var indexes = this.getStartEndIndexes("HitObjects")
for(var i = indexes.start; i <= indexes.end; i++){
circleID++
var values = this.data[i].split(",")
var emptyValue = false
var start = parseInt(values[this.osu.TIME])
var speed = this.difficulty.originalMultiplier
2018-09-20 01:20:26 +02:00
var gogoTime = false
2018-09-15 16:34:53 +02:00
var osuType = parseInt(values[this.osu.TYPE])
var hitSound = parseInt(values[this.osu.HITSOUND])
2018-09-27 17:31:57 +02:00
var beatLength = speed
2018-09-27 20:39:25 +02:00
var lastMultiplier = this.difficulty.lastMultiplier
var beatMS = this.beatInfo.beatInterval
2018-10-11 00:13:24 +02:00
if(circleID === 1 && start + this.offset < 0){
var offset = start + this.offset
this.soundOffset = offset
this.offset -= offset
}
2018-09-15 16:34:53 +02:00
for(var j = 0; j < this.timingPoints.length; j++){
var timingPoint = this.timingPoints[j]
if(j !== 0 && timingPoint.start - this.offset > start){
2018-09-15 16:34:53 +02:00
break
}
speed = timingPoint.sliderMultiplier
gogoTime = timingPoint.gogoTime
beatMS = timingPoint.beatMS
2018-09-15 16:34:53 +02:00
}
if(osuType & this.osu.SPINNER){
var endTime = parseInt(values[this.osu.ENDTIME])
var hitMultiplier = this.difficultyRange(this.difficulty.overallDifficulty, 3, 5, 7.5) * 1.65
var requiredHits = Math.floor(Math.max(1, (endTime - start) / 1000 * hitMultiplier))
2018-09-20 01:20:26 +02:00
circles.push(new Circle({
id: circleID,
2018-10-11 00:13:24 +02:00
start: start + this.offset,
2018-09-20 01:20:26 +02:00
type: "balloon",
txt: "ふうせん",
speed: speed,
2018-10-11 00:13:24 +02:00
endTime: endTime + this.offset,
2018-09-20 01:20:26 +02:00
requiredHits: requiredHits,
gogoTime: gogoTime,
beatMS: beatMS
2018-09-20 01:20:26 +02:00
}))
2018-09-15 16:34:53 +02:00
}else if(osuType & this.osu.SLIDER){
var extras = values.slice(this.osu.EXTRAS)
2018-09-27 17:31:57 +02:00
2018-09-27 20:39:25 +02:00
var distance = parseFloat(extras[this.osu.PIXELLENGTH])
var velocity = this.difficulty.sliderMultiplier * speed / 10
var endTime = start + distance / velocity
2018-09-27 17:31:57 +02:00
2018-09-15 16:34:53 +02:00
if(hitSound & this.osu.FINISH){
type = "daiDrumroll"
txt = "連打(大)ーっ!!"
}else{
type = "drumroll"
txt = "連打ーっ!!"
}
2018-09-20 01:20:26 +02:00
circles.push(new Circle({
id: circleID,
2018-10-11 00:13:24 +02:00
start: start + this.offset,
2018-09-20 01:20:26 +02:00
type: type,
txt: txt,
speed: speed,
2018-10-11 00:13:24 +02:00
endTime: endTime + this.offset,
gogoTime: gogoTime,
beatMS: beatMS
2018-09-20 01:20:26 +02:00
}))
2018-09-15 16:34:53 +02:00
}else if(osuType & this.osu.CIRCLE){
var type
var txt
2018-09-15 16:34:53 +02:00
if(hitSound & this.osu.FINISH){
if(hitSound & this.osu.WHISTLE || hitSound & this.osu.CLAP){
type = "daiKa"
txt = "カッ(大)"
}else if(hitSound & this.osu.NORMAL || hitSound === this.osu.FINISH){
2018-09-15 16:34:53 +02:00
type = "daiDon"
txt = "ドン(大)"
}else{
emptyValue = true
}
}else if(hitSound & this.osu.WHISTLE || hitSound & this.osu.CLAP){
type = "ka"
txt = "カッ"
}else if(hitSound & this.osu.NORMAL || hitSound === 0){
2018-09-15 16:34:53 +02:00
type = "don"
txt = "ドン"
}else{
emptyValue = true
}
if(!emptyValue){
2018-09-20 01:20:26 +02:00
circles.push(new Circle({
id: circleID,
2018-10-11 00:13:24 +02:00
start: start + this.offset,
2018-09-20 01:20:26 +02:00
type: type,
txt: txt,
speed: speed,
gogoTime: gogoTime,
beatMS: beatMS
2018-09-20 01:20:26 +02:00
}))
2018-09-15 16:34:53 +02:00
}
}else{
emptyValue = true
}
if(emptyValue){
console.warn("Unknown note type found on line " + (i + 1) + ": " + this.data[i])
}
}
return circles
}
}