class ImportSongs{ constructor(songSelect, event){ this.songSelect = songSelect this.songSelect.redrawRunning = false this.songSelect.pointer(false) this.loaderDiv = document.createElement("div") this.loaderDiv.innerHTML = assets.pages["loadsong"] loader.screen.appendChild(this.loaderDiv) var loadingText = document.getElementById("loading-text") loadingText.appendChild(document.createTextNode(strings.loading)) loadingText.setAttribute("alt", strings.loading) var files = [] for(var i = 0; i < event.target.files.length; i++){ files.push(event.target.files[i]) } var extensionRegex = /\.[^\/]+$/ files.sort((a, b) => { var path1 = a.webkitRelativePath.replace(extensionRegex, "") var path2 = b.webkitRelativePath.replace(extensionRegex, "") return path1 > path2 ? 1 : -1 }) this.tjaFiles = [] this.osuFiles = [] this.assetFiles = {} var metaFiles = [] this.otherFiles = {} this.songs = [] this.stylesheet = [] this.courseTypes = { "easy": 0, "normal": 1, "hard": 2, "oni": 3, "ura": 4 } this.categories = { "ボーカロイド曲": "ボーカロイド™曲", "ボーカロイド": "ボーカロイド™曲", "vocaloid music": "ボーカロイド™曲", "vocaloid": "ボーカロイド™曲", "バラエティー": "バラエティ", "どうよう": "バラエティ", "童謡・民謡": "バラエティ", "children": "バラエティ", "children/folk": "バラエティ", "children-folk": "バラエティ", "クラッシック": "クラシック", "classic": "クラシック" } for(var i in allStrings){ for(var ja in allStrings[i].categories){ this.categories[allStrings[i].categories[ja].toLowerCase()] = ja } } this.assetSelectors = { "bg-pattern-1": ".pattern-bg", "bg_genre_0": "#song-select", "title-screen": "#title-screen", "dancing-don": "#loading-don", "touch_drum": "#touch-drum-img", "touch_fullscreen": "#touch-full-btn", "touch_pause": "#touch-pause-btn", "bg_stage_1": ".song-stage-1", "bg_stage_2": ".song-stage-2", "bg_stage_3": ".song-stage-3" } for(var i = 0; i < files.length; i++){ var file = files[i] var name = file.name.toLowerCase() var path = file.webkitRelativePath.toLowerCase() if(name.endsWith(".tja")){ this.tjaFiles.push({ file: file, index: i }) }else if(name.endsWith(".osu")){ this.osuFiles.push({ file: file, index: i }) }else if(name === "genre.ini" || name === "box.def"){ var level = (file.webkitRelativePath.match(/\//g) || []).length metaFiles.push({ file: file, level: (level * 2) + (name === "genre.ini" ? 1 : 0) }) }else if(path.indexOf("/taiko-web assets/") !== -1){ if(!(name in this.assetFiles)){ this.assetFiles[name] = file } }else{ this.otherFiles[path] = file } } var metaPromises = [] metaFiles.forEach(fileObj => { metaPromises.push(this.addMeta(fileObj)) }) Promise.all(metaPromises).then(() => { var songPromises = [] this.tjaFiles.forEach(fileObj => { songPromises.push(this.addTja(fileObj)) }) this.osuFiles.forEach(fileObj => { songPromises.push(this.addOsu(fileObj)) }) songPromises.push(this.addAssets()) Promise.all(songPromises).then(this.loaded.bind(this)) }) } addMeta(fileObj){ var file = fileObj.file var level = fileObj.level var name = file.name.toLowerCase() var reader = new FileReader() var promise = pageEvents.load(reader).then(event => { var data = event.target.result.replace(/\0/g, "").split("\n") var category if(name === "genre.ini"){ var key for(var i = 0; i < data.length; i++){ var line = data[i].trim().toLowerCase() if(line.startsWith("[") && line.endsWith("]")){ key = line.slice(1, -1) }else if(key === "genre"){ var equalsPos = line.indexOf("=") if(equalsPos !== -1 && line.slice(0, equalsPos).trim() === "genrename"){ var value = line.slice(equalsPos + 1).trim() category = this.categories[value] || data[i].trim().slice(equalsPos + 1).trim() break } } } }else if(name === "box.def"){ for(var i = 0; i < data.length; i++){ var line = data[i].trim().toLowerCase() if(line.startsWith("#title:")){ var value = line.slice(7).trim() if(value in this.categories){ category = this.categories[value] } }else if(line.startsWith("#genre:")){ var value = line.slice(7).trim() category = this.categories[value] || data[i].trim().slice(7).trim() break } } } if(category){ var metaPath = file.webkitRelativePath.toLowerCase().slice(0, file.name.length * -1) var filesLoop = fileObj => { var tjaPath = fileObj.file.webkitRelativePath.toLowerCase().slice(0, fileObj.file.name.length * -1) if(tjaPath.startsWith(metaPath) && (!("categoryLevel" in fileObj) || fileObj.categoryLevel < level)){ fileObj.category = category fileObj.categoryLevel = level } } this.tjaFiles.forEach(filesLoop) this.osuFiles.forEach(filesLoop) } }).catch(() => {}) reader.readAsText(file, "sjis") return promise } addTja(fileObj){ var file = fileObj.file var index = fileObj.index var category = fileObj.category var reader = new FileReader() var promise = pageEvents.load(reader).then(event => { var data = event.target.result.replace(/\0/g, "").split("\n") var tja = new ParseTja(data, "oni", 0, true) var songObj = { id: index + 1, type: "tja", chart: data, stars: [] } var dir = file.webkitRelativePath.toLowerCase() dir = dir.slice(0, dir.lastIndexOf("/") + 1) var hasCategory = false for(var diff in tja.metadata){ var meta = tja.metadata[diff] songObj.title = songObj.title_en = meta.title || file.name.slice(0, file.name.lastIndexOf(".")) var subtitle = meta.subtitle || "" if(subtitle.startsWith("--")){ subtitle = subtitle.slice(2) } songObj.subtitle = songObj.subtitle_en = subtitle songObj.preview = meta.demostart ? Math.floor(meta.demostart * 1000) : 0 if(meta.level){ songObj.stars[this.courseTypes[diff]] = meta.level } if(meta.wave){ songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()] } if(meta.genre){ songObj.category = this.categories[meta.genre.toLowerCase()] || meta.genre } if(meta.taikowebskin){ songObj.song_skin = this.getSkin(dir, meta.taikowebskin) } } if(!songObj.category){ songObj.category = category || this.getCategory(file) } if(songObj.music && songObj.stars.filter(star => star).length !== 0){ this.songs[index] = songObj } }).catch(() => {}) reader.readAsText(file, "sjis") return promise } addOsu(fileObj){ var file = fileObj.file var index = fileObj.index var category = fileObj.category var reader = new FileReader() var promise = pageEvents.load(reader).then(event => { var data = event.target.result.replace(/\0/g, "").split("\n") var osu = new ParseOsu(data, 0, true) var dir = file.webkitRelativePath.toLowerCase() dir = dir.slice(0, dir.lastIndexOf("/") + 1) var songObj = { id: index + 1, type: "osu", chart: data, subtitle: osu.metadata.ArtistUnicode || osu.metadata.Artist, subtitle_en: osu.metadata.Artist || osu.metadata.ArtistUnicode, preview: osu.generalInfo.PreviewTime, stars: [null, null, null, parseInt(osu.difficulty.overallDifficulty) || 1], music: this.otherFiles[dir + osu.generalInfo.AudioFilename.toLowerCase()] } var filename = file.name.slice(0, file.name.lastIndexOf(".")) var title = osu.metadata.TitleUnicode || osu.metadata.Title if(title){ var suffix = "" var matches = filename.match(/\[.+?\]$/) if(matches){ suffix = " " + matches[0] } songObj.title = title + suffix songObj.title_en = (osu.metadata.Title || osu.metadata.TitleUnicode) + suffix }else{ songObj.title = filename } if(songObj.music){ this.songs[index] = songObj } songObj.category = category || this.getCategory(file) }).catch(() => {}) reader.readAsText(file) return promise } addAssets(){ return new Promise((resolve, reject) => { var promises = [] for(let name in this.assetFiles){ let id = this.getFilename(name) var file = this.assetFiles[name] if(name === "vectors.json"){ var reader = new FileReader() promises.push(pageEvents.load(reader).then(() => response => { vectors = JSON.parse(response) })) reader.readAsText(file) } if(assets.img.indexOf(name) !== -1){ let image = document.createElement("img") promises.push(pageEvents.load(image).then(() => { if(id in this.assetSelectors){ var selector = this.assetSelectors[id] this.stylesheet.push(selector + '{background-image:url("' + image.src + '")}') } })) image.id = name image.src = URL.createObjectURL(file) loader.assetsDiv.appendChild(image) assets.image[id].parentNode.removeChild(assets.image[id]) assets.image[id] = image } if(assets.audioSfx.indexOf(name) !== -1){ assets.sounds[id].clean() promises.push(this.loadSound(file, name, snd.sfxGain)) } if(assets.audioMusic.indexOf(name) !== -1){ assets.sounds[id].clean() promises.push(this.loadSound(file, name, snd.musicGain)) } if(assets.audioSfxLR.indexOf(name) !== -1){ assets.sounds[id + "_p1"].clean() assets.sounds[id + "_p2"].clean() promises.push(this.loadSound(file, name, snd.sfxGain).then(sound => { assets.sounds[id + "_p1"] = assets.sounds[id].copy(snd.sfxGainL) assets.sounds[id + "_p2"] = assets.sounds[id].copy(snd.sfxGainR) })) } if(assets.audioSfxLoud.indexOf(name) !== -1){ assets.sounds[id].clean() promises.push(this.loadSound(file, name, snd.sfxLoudGain)) } } Promise.all(promises).then(resolve, reject) }) } loadSound(file, name, gain){ var id = this.getFilename(name) return gain.load(file, true).then(sound => { assets.sounds[id] = sound }) } getFilename(name){ return name.slice(0, name.lastIndexOf(".")) } getCategory(file){ var path = file.webkitRelativePath.toLowerCase().split("/") for(var i = path.length - 2; i >= 0; i--){ for(var cat in this.categories){ if(path[i].indexOf(cat) !== -1){ return this.categories[cat] } } } } getSkin(dir, config){ var configArray = config.toLowerCase().split(",") var configObj = {} for(var i in configArray){ var string = configArray[i].trim() var space = string.indexOf(" ") if(space !== -1){ configObj[string.slice(0, space).trim()] = string.slice(space + 1).trim() } } if(!configObj.dir){ configObj.dir = "" } configObj.prefix = "custom " var skinnable = ["song", "stage", "don"] for(var i in skinnable){ var skinName = skinnable[i] var skinValue = configObj[skinName] if(skinValue && skinValue !== "none"){ var fileName = "bg_" + skinName + "_" + configObj.name var skinPath = this.joinPath(dir, configObj.dir, fileName) for(var j = 0; j < 2; j++){ if(skinValue !== "static"){ var suffix = (j === 0 ? "_a" : "_b") + ".png" }else{ var suffix = ".png" } var skinFull = this.normPath(skinPath + suffix) if(skinFull in this.otherFiles){ configObj[fileName + suffix] = this.otherFiles[skinFull] }else{ configObj[skinName] = null } if(skinValue === "static"){ break } } } } return configObj } loaded(){ this.songs = this.songs.filter(song => typeof song !== "undefined") if(this.stylesheet.length){ var style = document.createElement("style") style.appendChild(document.createTextNode(this.stylesheet.join("\n"))) document.head.appendChild(style) } if(this.songs.length){ var length = this.songs.length assets.songs = this.songs assets.customSongs = true assets.customSelected = 0 assets.sounds["se_don"].play() this.songSelect.clean() setTimeout(() => { loader.screen.removeChild(this.loaderDiv) this.clean() new SongSelect("browse", false, this.songSelect.touchEnabled) pageEvents.send("import-songs", length) }, 500) }else{ loader.screen.removeChild(this.loaderDiv) this.songSelect.browse.parentNode.reset() this.songSelect.redrawRunning = true this.clean() } } joinPath(){ var resultPath = arguments[0] for(var i = 1; i < arguments.length; i++){ var pPath = arguments[i] if(pPath && (pPath[0] === "/" || pPath[0] === "\\")){ resultPath = pPath }else{ var lastChar = resultPath.slice(-1) if(resultPath && (lastChar !== "/" || lastChar !== "\\")){ resultPath = resultPath + "/" } resultPath = resultPath + pPath } } return resultPath } normPath(path){ path = path.replace(/\\/g, "/").toLowerCase() while(path[0] === "/"){ path = path.slice(1) } var comps = path.split("/") for(var i = 0; i < comps.length; i++){ if(comps[i] === "." || comps[i] === ""){ comps.splice(i, 1) i-- }else if(i !== 0 && comps[i] === ".." && comps[i - 1] !== ".."){ comps.splice(i - 1, 2) i -= 2 } } return comps.join("/") } clean(){ delete this.loaderDiv delete this.songs delete this.tjaFiles delete this.osuFiles delete this.otherFiles } }