diff --git a/src/tja2fumen/converters.py b/src/tja2fumen/converters.py index 45c4c1e..65f3f8f 100644 --- a/src/tja2fumen/converters.py +++ b/src/tja2fumen/converters.py @@ -137,19 +137,17 @@ def convertTJAToFumen(tja): # Check to see if the measure contains a branching condition if measureTJA['branchStart']: - measureFumen['branchStart'] = measureTJA['branchStart'] - if measureFumen['branchStart']: - if measureFumen['branchStart'][0] == 'p': + if measureTJA['branchStart'][0] == 'p': if currentBranch == 'normal': idx_b1, idx_b2 = 0, 1 elif currentBranch == 'advanced': idx_b1, idx_b2 = 2, 3 elif currentBranch == 'master': idx_b1, idx_b2 = 4, 5 - measureFumen['branchInfo'][idx_b1] = int(total_notes_branch * measureFumen['branchStart'][1] * 20) - measureFumen['branchInfo'][idx_b2] = int(total_notes_branch * measureFumen['branchStart'][2] * 20) + measureFumen['branchInfo'][idx_b1] = int(total_notes_branch * measureTJA['branchStart'][1] * 20) + measureFumen['branchInfo'][idx_b2] = int(total_notes_branch * measureTJA['branchStart'][2] * 20) elif measureTJA['branchStart'][0] == 'r': - pass + measureFumen['branchInfo'] = measureTJA['branchStart'][1:] * 3 total_notes_branch = 0 total_notes_branch += note_counter_branch diff --git a/src/tja2fumen/parsers.py b/src/tja2fumen/parsers.py index 33dc53b..5873bd1 100644 --- a/src/tja2fumen/parsers.py +++ b/src/tja2fumen/parsers.py @@ -1,5 +1,6 @@ import os import re +from copy import deepcopy from tja2fumen.utils import readStruct, getBool, shortHex from tja2fumen.constants import NORMALIZE_COURSE, TJA_NOTE_TYPES, branchNames, noteTypes @@ -89,98 +90,87 @@ def getCourseData(lines): def parseCourseMeasures(lines): # Check if the course has branches or not hasBranches = True if [l for l in lines if l['name'] == 'BRANCHSTART'] else False - if hasBranches: - currentBranch = 'all' - targetBranch = 'all' - else: - currentBranch = 'normal' - targetBranch = 'normal' + currentBranch = 'all' if hasBranches else 'normal' flagLevelhold = False # Process course lines - branches = {'normal': [], 'advanced': [], 'master': []} - measureNotes = '' - measureEvents = [] + idx_m = 0 + idx_m_branchstart = 0 + emptyMeasure = {'data': '', 'events': []} + branches = {'normal': [deepcopy(emptyMeasure)], 'advanced': [deepcopy(emptyMeasure)], 'master': [deepcopy(emptyMeasure)]} for line in lines: # 1. Parse measure notes if line['name'] == 'NOTES': notes = line['value'] - # If measure has ended, then append the measure and start anew + # If measure has ended, then add notes to the current measure, then start a new one by incrementing idx_m if notes.endswith(','): - measureNotes += notes[0:-1] - measureCurrent = { - "data": measureNotes, - "events": measureEvents, - } - if currentBranch == 'all': - for branch in branches.keys(): - branches[branch].append(measureCurrent) - else: - branches[currentBranch].append(measureCurrent) - measureNotes = '' - measureEvents = [] - # Otherwise, keep tracking measureNotes + for branch in branches.keys() if currentBranch == 'all' else [currentBranch]: + branches[branch][idx_m]['data'] += notes[0:-1] + branches[branch].append(deepcopy(emptyMeasure)) + idx_m += 1 + # Otherwise, keep adding notes to the current measure ('idx_m') else: - measureNotes += notes + for branch in branches.keys() if currentBranch == 'all' else [currentBranch]: + branches[branch][idx_m]['data'] += notes - # 2. Parse commands - else: - # Measure commands + # 2. Parse measure commands that produce an "event" + elif line['name'] in ['GOGOSTART', 'GOGOEND', 'BARLINEON', 'BARLINEOFF', + 'SCROLL', 'BPMCHANGE', 'MEASURE', 'BRANCHSTART']: + # Get position of the event + for branch in branches.keys() if currentBranch == 'all' else [currentBranch]: + pos = len(branches[branch][idx_m]['data']) + + # Parse event type if line['name'] == 'GOGOSTART': - measureEvents.append({"name": 'gogo', "position": len(measureNotes), "value": '1'}) + currentEvent = {"name": 'gogo', "position": pos, "value": '1'} elif line['name'] == 'GOGOEND': - measureEvents.append({"name": 'gogo', "position": len(measureNotes), "value": '0'}) + currentEvent = {"name": 'gogo', "position": pos, "value": '0'} elif line['name'] == 'BARLINEON': - measureEvents.append({"name": 'barline', "position": len(measureNotes), "value": '1'}) + currentEvent = {"name": 'barline', "position": pos, "value": '1'} elif line['name'] == 'BARLINEOFF': - measureEvents.append({"name": 'barline', "position": len(measureNotes), "value": '0'}) + currentEvent = {"name": 'barline', "position": pos, "value": '0'} elif line['name'] == 'SCROLL': - measureEvents.append({"name": 'scroll', "position": len(measureNotes), "value": float(line['value'])}) + currentEvent = {"name": 'scroll', "position": pos, "value": float(line['value'])} elif line['name'] == 'BPMCHANGE': - measureEvents.append({"name": 'bpm', "position": len(measureNotes), "value": float(line['value'])}) + currentEvent = {"name": 'bpm', "position": pos, "value": float(line['value'])} elif line['name'] == 'MEASURE': - measureEvents.append({"name": 'measure', "position": len(measureNotes), "value": line['value']}) + currentEvent = {"name": 'measure', "position": pos, "value": line['value']} + elif line["name"] == 'BRANCHSTART': + if flagLevelhold: + continue + currentBranch = 'all' # Ensure that the #BRANCHSTART command is present for all branches + values = line['value'].split(',') + if values[0] == 'r': # r = drumRoll + values[1] = int(values[1]) # # of drumrolls + values[2] = int(values[2]) # # of drumrolls + elif values[0] == 'p': # p = Percentage + values[1] = float(values[1]) / 100 # % + values[2] = float(values[2]) / 100 # % + currentEvent = {"name": 'branchStart', "position": pos, "value": values} + idx_m_branchstart = idx_m # Preserve the index of the BRANCHSTART command to re-use for each branch - # Branch commands - elif line["name"] == 'START' or line['name'] == 'END': - if hasBranches: - currentBranch = 'all' - targetBranch = 'all' - else: - currentBranch = 'normal' - targetBranch = 'normal' + # Append event to the current measure's events + for branch in branches.keys() if currentBranch == 'all' else [currentBranch]: + branches[branch][idx_m]['events'].append(currentEvent) + + # 3. Parse commands that don't create an event (e.g. simply changing the current branch) + else: + if line["name"] == 'START' or line['name'] == 'END': + currentBranch = 'all' if hasBranches else 'normal' flagLevelhold = False elif line['name'] == 'LEVELHOLD': flagLevelhold = True elif line["name"] == 'N': currentBranch = 'normal' + idx_m = idx_m_branchstart elif line["name"] == 'E': currentBranch = 'advanced' + idx_m = idx_m_branchstart elif line["name"] == 'M': currentBranch = 'master' + idx_m = idx_m_branchstart elif line["name"] == 'BRANCHEND': - currentBranch = targetBranch - elif line["name"] == 'BRANCHSTART': - if flagLevelhold: - continue - values = line['value'].split(',') - if values[0] == 'r': - if len(values) >= 3: - targetBranch = 'master' - elif len(values) == 2: - targetBranch = 'advanced' - else: - targetBranch = 'normal' - elif values[0] == 'p': # p = percentage - values[1] = float(values[1]) / 100 # % - values[2] = float(values[2]) / 100 # % - measureEvents.append({"name": 'branchStart', "position": len(measureNotes), "value": values}) - if len(values) >= 3 and float(values[2]) <= 100: - targetBranch = 'master' - elif len(values) >= 2 and float(values[1]) <= 100: - targetBranch = 'advanced' - else: - targetBranch = 'normal' + currentBranch = 'all' # Ignored commands elif line['name'] == 'LYRIC': @@ -190,23 +180,16 @@ def parseCourseMeasures(lines): # Not implemented commands elif line['name'] == 'SECTION': - pass # TODO: Implement + pass # This seems to be inconsequential, but I'm not 100% sure. Need to test more branching fumens. elif line['name'] == 'DELAY': raise NotImplementedError else: raise NotImplementedError - # If there is measure data (i.e. the file doesn't end on a "measure end" symbol ','), append whatever is left - if measureNotes: - branches[currentBranch].append({ - "data": measureNotes, - "events": measureEvents, - }) - # Otherwise, if the file ends on a measure event (e.g. #GOGOEND), append any remaining events - elif measureEvents: - for event in measureEvents: - event['position'] = len(branches[len(branches) - 1]['data']) - branches[currentBranch][len(branches[currentBranch]) - 1]['events'].append(event) + # Delete the last measure in the branch if no notes or events were added to it (due to preallocating empty measures) + for branch in branches.values(): + if not branch[-1]['data'] and not branch[-1]['events']: + del branch[-1] # Merge measure data and measure events in chronological order for branchName, branch in branches.items(): diff --git a/testing/data/butou5.tja b/testing/data/butou5.tja new file mode 100644 index 0000000..bd78248 --- /dev/null +++ b/testing/data/butou5.tja @@ -0,0 +1,594 @@ +BPM:148 +OFFSET:-2.245 + +COURSE:Oni +LEVEL:8 +BALLOON: +SCOREINIT:410 +SCOREDIFF:100 + + +#START + + +#MEASURE 5/4 +500008000000000000000000000000000000000000000000000000000000, + +#MEASURE 4/4 +#BRANCHSTART r,1,2 +#N +3002202030022020, +3002202020000000, +1010101010102220, +1010222020000000, +12221222, +1020220220000000, +1010222010102220, +2202202020000000, +1011101010111010, +1002202010111020, +1002202020111010, +1002202022202000, +1011101010111010, +1002202010111020, +1022002200202000, +1020222010202220, +1011101010111010, +1002202010111020, +2220202020002000, +1002202010022020, +1011101010111010, +2202202010111020, +1010222010102220, +1020220220202000, +1010111110222000, +1010111120222000, +1010111220022000, +1022202012202000, +1010111220022000, +1010112210222000, +1010122110222000, +1000000011111010, +#GOGOSTART +1011101010111010, +1002202010111020, +1002202010202020, +1020202022202000, +1011101010111010, +1002202010111020, +1002102020021022, +2020202020202022, +1011101010111010, +1022202010111020, +1022202010222020, +1020202020020020, +1011101010111010, +1022202010111020, +1002202012112011, +2020220220001000, +#GOGOEND +12212222, +2020220220000000, +1010101010102220, +1020222010202000, +1002202010022020, +1010222020101000, +11111111, +33333000, +1022202220101022, +1022202220101022, +1022202220102220, +1110222011101000, +1022202011122020, +1010222011101000, +1120221011202210, +1122112211221122, +1002, +#GOGOSTART +33, +3022202020000000, +1012202010122020, +1012202022202000, +33, +3022202010221010, +1012202010202000, +1022102210222220, +1011101010111010, +1022202010112020, +1022202010222020, +1022202020020020, +1011101010111010, +1022202010211010, +1111222211112222, +1122112030303000, + +#E +3002202030022020, +3002202020000000, +1011101020102220, +1010222020000000, +32223222, +3020220220000000, +1010222010102220, +1201202020000000, +1022202010201122, +1002202010221020, +1002202012102000, +1002202022102000, +1022202010202211, +1022202010221020, +1022002210202000, +1002202010022020, +1022202010201122, +1002202010221020, +1221202020011010, +1002202022122020, +1022202010202120, +1102202010221020, +1010222010102220, +1020220220201000, +3030000000212000, +3030000022212000, +3030000020022000, +1002202212102000, +3030000020022000, +3030000022202000, +3030000022212000, +1111222210022020, +#GOGOSTART +1022202010201122, +1002202010221020, +1011202010112020, +1011202030303000, +1022202010201122, +1022202010221020, +3003003030111000, +34343434, +1022202010201122, +1002202010221020, +1011202010112020, +1011202022120020, +1022202010201122, +1022202010221020, +1012102102102000, +2020110110003000, +#GOGOEND +11111111, +1010110110000000, +1010101010102220, +1010222020101000, +1002202010022020, +1010222022101000, +32223222, +32223000, +2021202122101000, +1012122122102000, +1012101222102220, +1221221122102000, +2011201122102000, +1011221122102000, +1221221012212210, +2211221122112211, +2004, +#GOGOSTART +1022202010201122, +1002202010221020, +1002202020111000, +1002202011102000, +1022202010201122, +1002202010221020, +1002202011212000, +1022102210221111, +1022202010201122, +1022202010221020, +1011202010112020, +1011202022120020, +1022202010201122, +1022202010221020, +1221221112212211, +1221221110404000, + +#M +1002202010022020, +1002202010000000, +12221212, +1020122010000000, +12121212, +1020120210000000, +1010222010102220, +1201203040000000, +1011202020111020, +1011202020111010, +1011202020111010, +1022102210221010, +1011202020111020, +1011202010221020, +1011202010112020, +1011201120112020, +1112202020111020, +1112202020111010, +1112202020111010, +1022102210221010, +1112202020111020, +1112202010221020, +1012221010122210, +1012221210101000, +1010112210000000, +1010221120000000, +2020112210000000, +1002102012201000, +3030112210222000, +3030221120222000, +4040112210222000, +3000000011111000, +#GOGOSTART +1011202020111020, +1011202020111010, +2212101022121010, +2212101010101000, +1011202020111020, +1011202010221020, +1021221010222020, +2012201220122010, +1112202020111020, +1112202020111010, +2210101022101010, +2210101012012010, +1112202020111020, +1112202010221020, +1021221021221020, +1012112210201000, +#GOGOEND +12212222, +2020220210000002, +1020201020202220, +1020222010202000, +1002202010022020, +1010222022101000, +12212122, +12122000, +1011201122101000, +1011221122102000, +1022102211202010, +2211221120101000, +2011201122202000, +2011212122202000, +2222111122221111, +2211221122112211, +202020202020202220400000, +#GOGOSTART +33, +3000000000112020, +1112202020111010, +1022102210221000, +33, +3000000000221020, +1112202010122020, +2010121012101210, +1112202020111020, +1112202020111010, +2210201022102010, +2210101012012010, +1112202020111020, +1112202010221020, +1111222211112222, +1111222030303000, + +#BRANCHEND +#GOGOEND + + +#END + + +COURSE:Hard +LEVEL:5 +BALLOON:14,16 +SCOREINIT:520 +SCOREDIFF:137 + + +#START + + +#MEASURE 5/4 +, +#MEASURE 4/4 +1011100010111000, +10111000, +7, +00000800, +11101110, +1011101010000000, +7, +00000800, +1011100010001010, +1000000010111010, +1, +2022200020222000, +1011100010001010, +1000000010111010, +1, +1011100020222000, +1011100010001010, +1000000010111010, +1, +2022200020222000, +1011100010001010, +1000000010111010, +1, +1011100020222000, +1010111010000000, +1010111010000000, +1010222020000000, +11, +1010111010000000, +1010111010000000, +1010222020000000, +60000800, + +#GOGOSTART +1011101010001010, +1000000010111010, +1, +2022200020222000, +1011101010001010, +1000000010111010, +1, +1011100020222000, +1011101010001010, +1000000010111010, +1, +2022200020222000, +1011101010001010, +1000000010111010, +1, +1011100020222000, + +#GOGOEND +11022220, +11022220, +11022220, +11202220, +12201220, +11202000, +1111, +11111000, +2022202020002000, +2022202020002000, +22202220, +22222220, +2000200022202000, +2000200022202000, +22202220, +500000000000000000000000000000000000000000000008, +0, + +#GOGOSTART +30003011, +3000000010111010, +1, +2022200020222000, +30003011, +3000000010111010, +1, +1011100020222000, +1011101010001010, +1000000010111010, +1, +2022200020222000, +1011101010001010, +1000000010111010, +1110101011101010, +1110101030303000, + +#GOGOEND +, +, +#END + +COURSE:Normal +LEVEL:4 +BALLOON:10,12 +SCOREINIT:720 +SCOREDIFF:220 + +#START +#MEASURE 5/4 +, +#MEASURE 4/4 +11, +1110, +7, +00000800, +11, +1110, +7, +00000800, +10001011, +10001011, +1, +2220, +10001011, +10001011, +1, +2220, +10001011, +10001011, +1, +2220, +10001011, +10001011, +1, +22, +10111000, +10111000, +20222000, +11, +10111000, +10111000, +20222000, +60000800, + +#GOGOSTART +10001011, +10001011, +1, +2220, +10001011, +10001011, +1, +2220, +10001011, +10001011, +1, +2220, +10001011, +10001011, +1, +22, + +#GOGOEND +11000000, +11000000, +11000000, +11000000, +11, +11000000, +1111, +11101000, +2220, +2220, +2222, +22202000, +2220, +2220, +22202220, +500000000000000000000000000000000000000000000008, +, + +#GOGOSTART +33, +3, +1212, +2220, +33, +3, +1212, +2220, +10001011, +1, +1212, +2220, +10001011, +11, +11101110, +11103030, + +#GOGOEND +, +, +#END + +COURSE:Easy +LEVEL:3 +BALLOON:8,10 +SCOREINIT:670 +SCOREDIFF:255 + +#START +#MEASURE 5/4 +, +#MEASURE 4/4 +11, +1, +7, +00000800, +11, +11, +7, +00000800, +1011, +1, +1, +2220, +1011, +1, +1, +2220, +1011, +1, +1, +2220, +1011, +1, +1, +2220, +1, +1, +3, +11, +1, +1, +3, +60000800, + +#GOGOSTART +1011, +1, +1, +2220, +1011, +1, +1, +2220, +1011, +1, +1, +2220, +1011, +1, +1, +2220, + +#GOGOEND +1, +, +1, +, +11, +1, +1111, +1110, +2220, +2220, +2222, +22, +2220, +2220, +2222, +500000000000000000000000000000000000000000000008, +, + +#GOGOSTART +33, +3, +1, +2220, +33, +3, +1, +2220, +1011, +1, +1, +2220, +1011, +1, +11, +1033, + +#GOGOEND +, +, +#END \ No newline at end of file diff --git a/testing/data/butou5.zip b/testing/data/butou5.zip new file mode 100644 index 0000000..3b69b75 Binary files /dev/null and b/testing/data/butou5.zip differ diff --git a/testing/test_conversion.py b/testing/test_conversion.py index 80ba932..dd65abb 100644 --- a/testing/test_conversion.py +++ b/testing/test_conversion.py @@ -12,6 +12,7 @@ from tja2fumen.constants import COURSE_IDS, NORMALIZE_COURSE, simpleHeaders, byt @pytest.mark.parametrize('id_song', [ + pytest.param('butou5'), pytest.param('hol6po'), pytest.param('mikdp'), pytest.param('ia6cho'),