198 lines
8.4 KiB
Python
198 lines
8.4 KiB
Python
from pathlib import Path
|
|
|
|
from bidict import bidict
|
|
|
|
|
|
def strip_comments(code: str) -> str:
|
|
result = ''
|
|
index = 0
|
|
for line in code.splitlines():
|
|
comment_index = line.find('//')
|
|
if comment_index == -1:
|
|
result += line
|
|
elif comment_index != 0 and not line[:comment_index].isspace():
|
|
result += line[:comment_index]
|
|
index += 1
|
|
return result
|
|
|
|
def data_to_notes(data, diff):
|
|
note_start = -1
|
|
note_end = -1
|
|
target_found = False
|
|
diffs = {0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"}
|
|
diff_name = diffs.get(diff, "").lower()
|
|
|
|
i = 0
|
|
while i < len(data):
|
|
line = data[i]
|
|
|
|
if line.startswith("COURSE:"):
|
|
course_value = line[7:].strip().lower()
|
|
|
|
if (course_value.isdigit() and int(course_value) == diff) or course_value == diff_name:
|
|
target_found = True
|
|
else:
|
|
target_found = False
|
|
|
|
if target_found:
|
|
if line == "#START":
|
|
note_start = i + 1
|
|
elif line == "#END" and note_start != -1:
|
|
note_end = i
|
|
break
|
|
|
|
i += 1
|
|
|
|
notes = []
|
|
bar = []
|
|
for i in range(note_start, note_end):
|
|
line = data[i]
|
|
if line.startswith("#"):
|
|
bar.append(line)
|
|
else:
|
|
if line == ',':
|
|
if len(bar) == 0 or all(item.startswith('#') for item in bar):
|
|
bar.append('')
|
|
notes.append(bar)
|
|
bar = []
|
|
else:
|
|
item = line.strip(',')
|
|
bar.append(item)
|
|
if item != line:
|
|
notes.append(bar)
|
|
bar = []
|
|
return notes, note_start, note_end
|
|
|
|
def validate_tja(tja_file: Path, check_watermark=False):
|
|
diffs = bidict({0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"})
|
|
try:
|
|
tja_file.read_text(encoding='utf-8-sig')
|
|
except Exception as e:
|
|
print(f'Incorrect Encoding: {tja_file}')
|
|
lines = tja_file.read_text(encoding='utf-8-sig').splitlines()
|
|
if check_watermark and lines[0] != "//TJADB Project":
|
|
print(f'Missing Watermark: {tja_file}')
|
|
tja_data = [cleaned for line in lines if (cleaned := strip_comments(line).strip())]
|
|
current_diff = None
|
|
balloon_data = []
|
|
for i, line in enumerate(tja_data):
|
|
if line.startswith('TITLE'):
|
|
if line.split(':')[1] == 'FIXME':
|
|
print(f'FIXME name: {tja_file}')
|
|
elif line.startswith('SUBTITLE:'):
|
|
subtitle_display = line.split(':')[1][0:2]
|
|
if subtitle_display != '--' and subtitle_display != '++':
|
|
print(f'Subtitle Display Type Missing: {tja_file}, Line {i}')
|
|
elif line.startswith('WAVE:'):
|
|
wave_path = tja_file.parent / line.split(':')[1]
|
|
if not wave_path.exists():
|
|
print(f'WAVE Path Invalid: {tja_file}, Line {i}')
|
|
elif wave_path.suffix != '.ogg':
|
|
print(f'WAVE is not an OGG: {tja_file}, Line {i}')
|
|
elif line.startswith('DEMOSTART:'):
|
|
if float(line.split(':')[1]) < 0:
|
|
print(f'DEMOSTART value is negative: {tja_file}, Line {i}')
|
|
elif line.startswith('SONGVOL:'):
|
|
if float(line.split(':')[1]) < 0:
|
|
print(f'SONGVOL value is negative: {tja_file}, Line {i}')
|
|
elif line.startswith('SEVOL:'):
|
|
if float(line.split(':')[1]) < 0:
|
|
print(f'SEVOL value is negative: {tja_file}, Line {i}')
|
|
elif line.startswith('BPM:'):
|
|
if float(line.split(':')[1]) < 0:
|
|
print(f'BPM value is negative: {tja_file}, Line {i}')
|
|
elif float(line.split(':')[1]) == 0:
|
|
print(f'BPM value is 0: {tja_file}')
|
|
elif line.split(':')[1] == '':
|
|
print(f'BPM value is missing: {tja_file}, Line {i}')
|
|
elif line.startswith('BGMOVIE:'):
|
|
movie_path = tja_file.parent / line.split(':')[1]
|
|
if not movie_path.exists():
|
|
print(f'BGMOVIE Path Invalid: {tja_file}, Line {i}')
|
|
elif line.startswith('COURSE:'):
|
|
if line.split(':')[1].isdigit():
|
|
if not (0 <= int(line.split(':')[1]) <= 6):
|
|
print(f'COURSE Name Invalid: {tja_file}, Line {i}')
|
|
elif line.split(':')[1].lower() not in diffs.inverse:
|
|
print(f'COURSE Name Invalid: {tja_file}, Line {i}')
|
|
else:
|
|
if line.split(':')[1].isdigit():
|
|
current_diff = int(line.split(':')[1])
|
|
else:
|
|
current_diff = diffs.inverse[line.split(':')[1].lower()]
|
|
elif line.startswith('LEVEL:'):
|
|
if current_diff is None:
|
|
print(f'LEVEL was specified with no COURSE: {tja_file}, Line {i}')
|
|
else:
|
|
if current_diff == 0:
|
|
if not (1 <= int(line.split(':')[1]) <= 6):
|
|
print(f'Invalid LEVEL range for COURSE:{current_diff} : {tja_file}, Line {i}')
|
|
if current_diff == 1:
|
|
if not (1 <= int(line.split(':')[1]) <= 7):
|
|
print(f'Invalid LEVEL range for COURSE:{current_diff} : {tja_file}, Line {i}')
|
|
if current_diff == 2:
|
|
if not (1 <= int(line.split(':')[1]) <= 8):
|
|
print(f'Invalid LEVEL range for COURSE:{current_diff} : {tja_file}, Line {i}')
|
|
if current_diff >= 3:
|
|
if not (1 <= int(line.split(':')[1]) <= 10):
|
|
print(f'Invalid LEVEL range for COURSE:{current_diff} : {tja_file}, Line {i}')
|
|
elif line.startswith('BALLOON:'):
|
|
if line.split(':')[1] == '':
|
|
continue
|
|
for balloon in line.split(':')[1].split(','):
|
|
if not balloon.isdigit():
|
|
print(f'Invalid Balloon: {tja_file}, Line {i}')
|
|
else:
|
|
balloon_data.append(int(balloon))
|
|
elif line.startswith('SCOREINIT:'):
|
|
for scoreinit in line.split(':')[1].split(','):
|
|
if not scoreinit.isdigit():
|
|
print(f'Invalid SCOREINIT Value: {tja_file}, Line {i}')
|
|
elif int(scoreinit) <= 0:
|
|
print(f'Invalid SCOREINIT Value: {tja_file}, Line {i}')
|
|
elif line.startswith('SCOREDIFF:'):
|
|
if not line.split(':')[1].isdigit():
|
|
print(f'Invalid SCOREDIFF Value: {tja_file}, Line {i}')
|
|
elif int(line.split(':')[1]) < 0:
|
|
print(f'Invalid SCOREDIFF Value: {tja_file}, Line {i}')
|
|
for i in range(6):
|
|
notes, note_start, note_end = data_to_notes(tja_data, i)
|
|
index = 0
|
|
balloon_index = 0
|
|
in_hold_note = False
|
|
if notes != []:
|
|
for bar in notes:
|
|
for part in bar:
|
|
if part.startswith('#BPMCHANGE'):
|
|
bpm = part.split()[1]
|
|
if float(bpm) <= 0:
|
|
print(f'BPMCHANGE Invalid: {tja_file}, Line {index+note_start}')
|
|
elif part.startswith('#MEASURE'):
|
|
divisor = part.find('/')
|
|
if float(part[divisor+1:]) == 0:
|
|
print(f'Time Signature Invalid: {tja_file}, Line {index+note_start}')
|
|
elif part.startswith('#'):
|
|
continue
|
|
else:
|
|
for note in part:
|
|
if not note.isdigit():
|
|
print(f'Invalid Note Type {note}: {tja_file}, Line {index+note_start}')
|
|
continue
|
|
if note == '7' or note == '9':
|
|
balloon_index += 1
|
|
if balloon_index > len(balloon_data):
|
|
print(f'Balloon Data Missing: {tja_file}, Line {index+note_start}')
|
|
if note == '8':
|
|
in_hold_note = False
|
|
elif 5 <= int(note) <= 9:
|
|
if in_hold_note:
|
|
print(f'Drumroll/Balloon Tail Missing or Head Repeated: {tja_file}, Line {index+note_start}')
|
|
in_hold_note = True
|
|
index += 1
|
|
|
|
|
|
|
|
for file in (Path.cwd()).rglob('*.tja'):
|
|
validate_tja(file, check_watermark=True)
|
|
#print(f'{file} is valid')
|