Skip to content

Instantly share code, notes, and snippets.

@Aikoyori
Created May 20, 2021 13:59
Show Gist options
  • Save Aikoyori/5e6709229eda5a772555b038ed237301 to your computer and use it in GitHub Desktop.
Save Aikoyori/5e6709229eda5a772555b038ed237301 to your computer and use it in GitHub Desktop.
mania8KtoFNF source code
import os
import json
import numpy as np
import sys
class HitObject:
def __init__(self, code, globaloffset):
data = code.split(",")
self.lane = int(data[0])
self.globaloffset = globaloffset
self.samplesound = int(data[1])
try:
self.offset = int(data[2])
except:
self.offset = data[2]
self.type = int(data[3])
self.hitsound = int(data[4])
self.release = -1
if self.type == 128:
try:
self.release = int(data[-1].split(":")[0])
except:
self.release = data[-1].split(":")[0]
def get_column_8k(self):
return max(0, (self.lane - 32) // 64)
def encode(self):
a = "0:0:0:0:"
if self.type == 128:
a = str(self.release) + ":" + a
return "{},{},{},{},{},{}".format(self.lane, self.samplesound, self.offset, self.type, self.hitsound, a)
def to_fnf_hitobject(self):
return [self.offset - self.globaloffset, self.get_column_8k(), self.release - self.offset if self.type == 128 else 0]
class TimingPoint:
def __init__(self, code):
data = code.split(",")
self.offset = float(data[0])
self.millisec = float(data[1])
self.isBPM = int(data[-2])
if self.isBPM == 1:
self.velocity = 60000 / float(data[1])
else:
self.velocity = -100 / float(data[1])
self.timeSignature = int(data[2])
self.hitsoundvolume = int(data[5])
self.isKiai = int(data[-1])
def switch(self, mainbpm):
if self.isBPM == 1:
self.velocity = self.velocity / mainbpm
self.isBPM = 0
else:
self.velocity = mainbpm * self.velocity
self.isBPM = 1
def encode(self):
vel = 60000 / self.velocity if self.isBPM == 1 else -100 / self.velocity
return "{},{},{},1,0,{},{},{}".format(self.offset, vel, self.timeSignature, self.hitsoundvolume, self.isBPM, self.isKiai)
class osufile:
def __init__(self, data: str):
self.data = data.split("\n")
self.General = None
self.editor = None
self.metadata = None
self.difficulty = None
self.TimingPoints = list()
self.HitObjects = list()
self.initialize_data()
def initialize_data(self):
self.parseGeneral()
self.parseEditor()
self.parseMetadata()
self.parseDifficulty()
self.parseTimingPoints()
self.parseHitObjects()
def parseGroup(self, keyword):
start = self.data.index("[{}]".format(keyword)) + 1
end = self.data[start:].index("") + start
group = dict()
for data in self.data[start:end]:
d = data.split(":")
try:
group[d[0]] = int(d[1])
except:
try:
group[d[0]] = float(d[1])
except:
group[d[0]] = d[1]
return group
def parseGeneral(self):
self.General = self.parseGroup("General")
def parseEditor(self):
self.editor = self.parseGroup("Editor")
if "Bookmarks" in self.editor.keys():
self.editor["Bookmarks"] = [int(k) for k in self.editor["Bookmarks"].split(",")]
def parseMetadata(self):
self.metadata = self.parseGroup("Metadata")
self.metadata["Tags"] = self.metadata["Tags"].split(" ")
def parseDifficulty(self):
self.difficulty = self.parseGroup("Difficulty")
def parseTimingPoints(self):
start = self.data.index("[TimingPoints]") + 1
end = self.data[start:].index("") + start
for data in self.data[start:end]:
self.TimingPoints.append(TimingPoint(data))
def parseHitObjects(self):
start = self.data.index("[HitObjects]") + 1
end = self.data[start:].index("") + start
for data in self.data[start:end]:
self.HitObjects.append(HitObject(data,int(self.TimingPoints[0].offset)))
def get_hitobjects(self, start, end):
return list(filter(lambda x: start <= x.offset < end, self.HitObjects))
def to_fnf(self):
isPlayer = False
if self.HitObjects[0].get_column_8k() >= 4:
isPlayer = True
Notes = []
secLen = 16
for start in range(0,self.HitObjects[len(self.HitObjects)-1].offset,secLen//4*int(self.TimingPoints[0].millisec)):
end = start + self.TimingPoints[0].millisec*secLen//4
section = self.get_hitobjects(start, end)
sectionNote = {
"lengthInSteps": secLen,
"mustHitSection": isPlayer,
"sectionNotes": [note.to_fnf_hitobject() for note in section]
}
# isPlayer = not isPlayer
Notes.append(sectionNote)
fnf = {"song": {"song": f"{self.metadata['Title']}", "bpm": int(self.TimingPoints[0].velocity), "needsVoices": True,
"player1": "bf", "player2": "bf-pixel", "speed": self.difficulty['HPDrainRate'], "notes": Notes},"generatedBy":"SNIFF ver.6"}
return fnf
def parse_beatmap(file):
# this doesn't work with japanese and unicode metadata
with open(file, "r") as f:
data = f.read()
return osufile(data)
if __name__ == "__main__":
beatmap = parse_beatmap(sys.argv[1])
json.dump(beatmap.to_fnf(), open((beatmap.metadata['Title'])+"-"+(beatmap.metadata['Version'])+".json", 'w+'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment