1
0
mirror of synced 2025-02-03 13:13:26 +01:00

parsetja.py: Slightly refactor the getCourse function

The big if/elses were a little hard to parse, so I broke things
into subfunctions.
This commit is contained in:
Viv 2023-06-02 16:33:43 -04:00
parent 35ba7bfeba
commit b7670b54a3

View File

@ -5,20 +5,108 @@ import re
# Valid strings for headers and chart commands # Valid strings for headers and chart commands
HEADER_GLOBAL = ['TITLE', 'TITLEJA', 'SUBTITLE', 'SUBTITLEJA', 'BPM', 'WAVE', 'OFFSET', 'DEMOSTART', 'GENRE'] HEADER_GLOBAL = ['TITLE', 'TITLEJA', 'SUBTITLE', 'SUBTITLEJA', 'BPM', 'WAVE', 'OFFSET', 'DEMOSTART', 'GENRE']
HEADER_COURSE = ['COURSE', 'LEVEL', 'BALLOON', 'SCOREINIT', 'SCOREDIFF', 'TTRO' 'WBEAT'] HEADER_COURSE = ['COURSE', 'LEVEL', 'BALLOON', 'SCOREINIT', 'SCOREDIFF', 'TTRO' 'WBEAT']
COMMAND = ['START', 'END', 'GOGOSTART', 'GOGOEND', 'BRANCHSTART', 'BRANCHEND', 'BARLINEON', 'BARLINEOFF', 'MEASURE', BRANCH_COMMANDS = ['START', 'END', 'BRANCHSTART', 'BRANCHEND', 'N', 'E', 'M']
'BPMCHANGE', 'DELAY', 'SECTION', 'N', 'E', 'M', 'LEVELHOLD', 'SCROLL', 'BMSCROLL', 'HBSCROLL', 'TTBREAK'] MEASURE_COMMANDS = ['MEASURE', 'GOGOSTART', 'GOGOEND', 'SCROLL', 'BPMCHANGE', 'TTBREAK' 'LEVELHOLD']
UNUSED_COMMANDS = ['DELAY', 'SECTION', 'BMSCROLL', 'HBSCROLL', 'BARLINEON', 'BARLINEOFF']
COMMAND = BRANCH_COMMANDS + MEASURE_COMMANDS + UNUSED_COMMANDS
def getCourse(tjaHeaders, lines): def getCourse(tjaHeaders, lines):
headers = { def parseHeaderMetadata(line):
"course": 'Oni', nonlocal headers
"level": 0, if line["name"] == 'COURSE':
"balloon": [], headers['course'] = line['value']
"scoreInit": 100, elif line["name"] == 'LEVEL':
"scoreDiff": 100, headers['level'] = int(line['value'])
"ttRowBeat": 16, elif line["name"] == 'SCOREINIT':
} headers['scoreInit'] = int(line['value'])
elif line["name"] == 'SCOREDIFF':
headers['scoreDiff'] = int(line['value'])
elif line["name"] == 'TTROWBEAT':
headers['ttRowBeat'] = int(line['value'])
elif line["name"] == 'BALLOON':
if line['value']:
balloons = [int(v) for v in line['value'].split(",")]
else:
balloons = []
headers['balloon'] = balloons
def parseBranchCommands(line):
nonlocal flagLevelhold, targetBranch, currentBranch
if line["name"] == 'BRANCHSTART':
if flagLevelhold:
return
values = line['value'].split(',')
if values[0] == 'r':
if len(values) >= 3:
targetBranch = 'M'
elif len(values) == 2:
targetBranch = 'E'
else:
targetBranch = 'N'
elif values[0] == 'p':
if len(values) >= 3 and float(values[2]) <= 100:
targetBranch = 'M'
elif len(values) >= 2 and float(values[1]) <= 100:
targetBranch = 'E'
else:
targetBranch = 'N'
elif line["name"] == 'BRANCHEND':
currentBranch = targetBranch
elif line["name"] == 'N':
currentBranch = 'N'
elif line["name"] == 'E':
currentBranch = 'E'
elif line["name"] == 'M':
currentBranch = 'M'
elif line["name"] == 'START' or line['name'] == 'END':
currentBranch = 'N'
targetBranch = 'N'
flagLevelhold = False
def parseMeasureCommands(line):
nonlocal measureDivisor, measureDividend, measureEvents, measureProperties, flagLevelhold
if line['name'] == 'MEASURE':
matchMeasure = re.match(r"(\d+)/(\d+)", line['value'])
if not matchMeasure:
return
measureDividend = int(matchMeasure.group(1))
measureDivisor = int(matchMeasure.group(2))
elif line['name'] == 'GOGOSTART':
measureEvents.append({"name": 'gogoStart', "position": len(measureData)})
elif line['name'] == 'GOGOEND':
measureEvents.append({"name": 'gogoEnd', "position": len(measureData)})
elif line['name'] == 'SCROLL':
measureEvents.append({"name": 'scroll', "position": len(measureData), "value": float(line['value'])})
elif line['name'] == 'BPMCHANGE':
measureEvents.append({"name": 'bpm', "position": len(measureData), "value": float(line['value'])})
elif line['name'] == 'TTBREAK':
measureProperties['ttBreak'] = True
elif line['name'] == 'LEVELHOLD':
flagLevelhold = True
def parseMeasureData(line):
nonlocal measures, measureData, measureDividend, measureDivisor, measureEvents, measureProperties
data = line['data']
# If measure has ended, then append the measure and start anew
if data.endswith(','):
measureData += data[0:-1]
measure = {
"length": [measureDividend, measureDivisor],
"properties": measureProperties,
"data": measureData,
"events": measureEvents,
}
measures.append(measure)
measureData = ''
measureEvents = []
measureProperties = {}
# Otherwise, keep tracking measureData
else:
measureData += data
# Define state variables
headers = {}
measures = [] measures = []
measureDividend = 4 measureDividend = 4
measureDivisor = 4 measureDivisor = 4
@ -29,167 +117,31 @@ def getCourse(tjaHeaders, lines):
targetBranch = 'N' targetBranch = 'N'
flagLevelhold = False flagLevelhold = False
# Process lines # Process course lines
for line in lines: for line in lines:
if line["type"] == 'header': if line["type"] == 'header':
if line["name"] == 'COURSE': parseHeaderMetadata(line)
headers['course'] = line['value'] elif line["type"] == 'command' and line['name'] in BRANCH_COMMANDS:
parseBranchCommands(line)
elif line["name"] == 'LEVEL': elif line["type"] == 'command' and line['name'] in MEASURE_COMMANDS and currentBranch == targetBranch:
headers['level'] = int(line['value']) parseMeasureCommands(line)
elif line['type'] == 'data' and currentBranch == targetBranch:
elif line["name"] == 'BALLOON': parseMeasureData(line)
if line['value']:
balloons = [int(v) for v in line['value'].split(",")]
else:
balloons = []
headers['balloon'] = balloons
elif line["name"] == 'SCOREINIT':
headers['scoreInit'] = int(line['value'])
elif line["name"] == 'SCOREDIFF':
headers['scoreDiff'] = int(line['value'])
elif line["name"] == 'TTROWBEAT':
headers['ttRowBeat'] = int(line['value'])
elif line["type"] == 'command': # Post-processing: Ensure the first measure has a BPM event
if line["name"] == 'BRANCHSTART': if measures:
if flagLevelhold:
continue
values = line['value'].split(',')
if values[0] == 'r':
if len(values) >= 3:
targetBranch = 'M'
elif len(values) == 2:
targetBranch = 'E'
else:
targetBranch = 'N'
elif values[0] == 'p':
if len(values) >= 3 and float(values[2]) <= 100:
targetBranch = 'M'
elif len(values) >= 2 and float(values[1]) <= 100:
targetBranch = 'E'
else:
targetBranch = 'N'
elif line["name"] == 'BRANCHEND':
currentBranch = targetBranch
elif line["name"] == 'N':
currentBranch = 'N'
elif line["name"] == 'E':
currentBranch = 'E'
elif line["name"] == 'M':
currentBranch = 'M'
elif line["name"] == 'START':
currentBranch = 'N'
targetBranch = 'N'
flagLevelhold = False
elif line["name"] == 'END':
currentBranch = 'N'
targetBranch = 'N'
flagLevelhold = False
else:
if currentBranch != targetBranch:
continue
if line['name'] == 'MEASURE':
matchMeasure = re.match(r"(\d+)/(\d+)", line['value'])
if not matchMeasure:
continue
measureDividend = int(matchMeasure.group(1))
measureDivisor = int(matchMeasure.group(2))
elif line['name'] == 'GOGOSTART':
measureEvents.append({
"name": 'gogoStart',
"position": len(measureData),
})
elif line['name'] == 'GOGOEND':
measureEvents.append({
"name": 'gogoEnd',
"position": len(measureData),
})
elif line['name'] == 'SCROLL':
measureEvents.append({
"name": 'scroll',
"position": len(measureData),
"value": float(line['value']),
})
elif line['name'] == 'BPMCHANGE':
measureEvents.append({
"name": 'bpm',
"position": len(measureData),
"value": float(line['value']),
})
elif line['name'] == 'TTBREAK':
measureProperties['ttBreak'] = True
elif line['name'] == 'LEVELHOLD':
flagLevelhold = True
else:
print(line['name']) # Unknown: BARLINEOFF, BARLINEON
elif line['type'] == 'data' and currentBranch is targetBranch:
data = line['data']
if data.endswith(','):
measureData += data[0:-1]
measure = {
"length": [measureDividend, measureDivisor],
"properties": measureProperties,
"data": measureData,
"events": measureEvents,
}
measures.append(measure)
measureData = ''
measureEvents = []
measureProperties = {}
else:
measureData += data
if len(measures):
# Make first BPM event
firstBPMEventFound = False firstBPMEventFound = False
# Search for BPM event in the first measure # Search for BPM event in the first measure
for i in range(len(measures[0]['events'])): for i in range(len(measures[0]['events'])):
evt = measures[0]['events'][i] evt = measures[0]['events'][i]
if evt.name == 'bpm' and evt.position == 0: if evt.name == 'bpm' and evt.position == 0:
firstBPMEventFound = True firstBPMEventFound = True
# If not present, insert a BPM event into the first measure using the global header metadata
if not firstBPMEventFound: if not firstBPMEventFound:
# noinspection PyTypeChecker # noinspection PyTypeChecker
measures[0]['events'].insert(0, { measures[0]['events'].insert(0, {"name": 'bpm', "position": 0, "value": tjaHeaders['bpm']})
"name": 'bpm',
"position": 0,
"value": tjaHeaders['bpm'],
})
# Helper values
course = 0
courseValue = headers['course'].lower()
if courseValue in ['easy', '0']:
course = 0
elif courseValue in ['normal', '1']:
course = 1
elif courseValue in ['hard', '2']:
course = 2
elif courseValue in ['oni', '3']:
course = 3
elif courseValue in ['ura', 'edit', '4']:
course = 4
# Post-processing: In case the file doesn't end on a "measure end" symbol (','), append whatever is left
if measureData: if measureData:
measures.append({ measures.append({
"length": [measureDividend, measureDivisor], "length": [measureDividend, measureDivisor],
@ -197,15 +149,15 @@ def getCourse(tjaHeaders, lines):
"data": measureData, "data": measureData,
"events": measureEvents, "events": measureEvents,
}) })
else:
# Post-processing: Otherwise, if the file ends on a measure event (e.g. #GOGOEND), append any remaining events
elif measureEvents:
for event in measureEvents: for event in measureEvents:
event['position'] = len(measures[len(measures) - 1]['data']) event['position'] = len(measures[len(measures) - 1]['data'])
# noinspection PyTypeChecker # noinspection PyTypeChecker
measures[len(measures) - 1]['events'].append(event) measures[len(measures) - 1]['events'].append(event)
# Output return headers, measures
print(measures[len(measures) - 1])
return course, headers, measures
def parseLine(line): def parseLine(line):
@ -249,7 +201,7 @@ def parseTJA(tja):
currentCourse = '' currentCourse = ''
for line in lines: for line in lines:
parsed = parseLine(line) parsed = parseLine(line)
# Case 1: Comments (ignore # Case 1: Comments (ignore)
if parsed['type'] == 'comment': if parsed['type'] == 'comment':
pass pass
# Case 2: Global header metadata # Case 2: Global header metadata