3 DIR
="$( cd "$
( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
4 source "$DIR/config.sh"
10 songs_file
=$
(ls "$DIR" |
grep songs |
tail -n1
)
12 if [ -z
"$songs_file" ]; then
16 sed -i
'1s/.*/<?xml version="1.0" encoding="ISO-8859-1" ?>/' "${path_to_stats}"
17 difficulties
=(Challenge Edit Hard Medium Easy
)
18 step_types
=("dance-double" "dance-single")
19 tiers
=("1.00" "0.99" "0.98" "0.96" "0.94" "0.92" "0.89" "0.86" "0.83" "0.80" "0.76" "0.72" "0.68" "0.64" "0.60" "0.55")
21 if [ "$1" = "push" ]; then
22 declare -A all_song_hashes
25 # pretty sure this can be merged with the shit below somehow
26 if find "${DIR}"/*.favourites.txt
-type f
-print
-quit
2>/dev
/null |
grep -q .
; then
28 while read -d $
'\0' file; do
29 IFS
=$
'\n' read -d
'' -r
-a lines
< $file
30 basename=$
(basename "$file")
31 person
="${basename%.*.*}"
32 if [ "$person" = "$me" ]; then
33 persons_folder
="$faves_folder"
35 formatted_person
="$(tr '[:lower:]' '[:upper:]' <<< ${person:0:1})${person:1}"
36 persons_folder
=${external_favourite_naming_scheme/\%person\%/$formatted_person}
38 for i
in "${lines[@]}"; do
39 all_song_hashes
["$i"]+="$persons_folder"
40 pattern
="${pattern}|$i"
42 done < <(find "${DIR}"/*.favourites.txt
-type f
-print0
)
45 pattern
="${pattern:1}"
46 song_paths
=$
(grep -E
"$pattern" "${DIR}/${songs_file}" |
sort -u
-k1
,1)
48 while read -r song_path
; do
49 hash=$
(echo $song_path | cut
-sd
' ' -f1
)
50 path
=$
(echo $song_path | cut
-sd
/ -f6
)
51 hash_map
["${all_song_hashes[$hash]}/$path"]+="$hash"
52 done <<< "$song_paths"
54 if find "${DIR}"/*.favourites.txt
-type f
-print
-quit
2>/dev
/null |
grep -q .
; then
55 while read -d $
'\0' file; do
56 file=$
(basename "$file")
58 IFS
=$
'\n' read -d
'' -r
-a lines
< "${DIR}/$file"
60 if [ "$person" = "$me" ]; then
61 persons_folder
="$faves_folder"
63 formatted_person
="$(tr '[:lower:]' '[:upper:]' <<< ${person:0:1})${person:1}"
64 persons_folder
=${external_favourite_naming_scheme/\%person\%/$formatted_person}
67 xmlstarlet_meta_cmd
="$xmlstarlet_meta_cmd -v \"//SongScores//Song[starts-with(@Dir, \\\"Songs/${persons_folder}\\\")][Steps//PercentDP]//@*\" -n"
68 xmlstarlet_scores_cmd
="$xmlstarlet_scores_cmd -o \"SCORES\" -n -v \"//SongScores//Song[starts-with(@Dir, \\\"Songs/${persons_folder}\\\")]//HighScore[1]//PercentDP\" -n -o \"NAMES\" -n -v \"//SongScores/Song[starts-with(@Dir, \\\"Songs/${persons_folder}\\\")]//HighScore[1]//Name\" -n"
69 done < <(find "${DIR}"/*.favourites.txt
-type f
-print0
)
71 meta
=$
(eval "xmlstarlet sel -I -t $xmlstarlet_meta_cmd -o \"Songs\" \"${path_to_stats}\"")
72 scores
=$
(eval "xmlstarlet sel -I -t $xmlstarlet_scores_cmd \"${path_to_stats}\"")
73 nulls
=$
(eval "xmlstarlet sel -t -v \"//LastPlayed[text()=\\\"1933-01-02\\\"]/ancestor::Song/@Dir\" \"${path_to_stats}\"")
75 declare -a null_hashes
77 if ! [ -z
"$nulls" ]; then
78 while read -r null
; do
79 the_path
=$
(echo "$null" | cut
-sd
/ -f2
,3 |
sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g; s/'/\'"'"'/g')
80 if ! [ -z "${hash_map[$the_path]}" ]; then
81 null_hashes+=("${hash_map[$the_path]}")
96 scores_or_names="SCORES
"
99 if [ "$s" = "SCORES
" ]; then
100 scores_or_names="SCORES
"
104 if [ "$s" = "NAMES
" ]; then
105 scores_or_names="NAMES
"
109 if ! [ "$scores_or_names" = "SCORES
" ]; then
114 if ! [ "$scores_or_names" = "NAMES
" ]; then
121 if [[ ${m:0:5} = "Songs
" ]]; then
122 for dd in "${!d[@]}"; do
123 if ! [ -z "${mmeta["${song_hash}:${d[$dd]}:${t[$dd]}"]}" ]; then
124 echo "!!!! that key exists already..."
127 mmeta
["${song_hash}:${d[$dd]}:${t[$dd]}"]="${names[$bigboi_counter]}:${sscores[$bigboi_counter]}"
128 bigboi_counter
=$
((bigboi_counter
+1))
134 if [[ ${m:0:6} = "Songs/" ]]; then
135 plain_song
=$
(echo $m |
sed 's/&/\&/g;' | cut
-sd
/ -f2
,3)
136 song_hash
=${hash_map[$plain_song]}
141 if (( $counter % 2 )); then
146 counter
=$
((counter
+1))
149 # this is a list of all hashes and who has the score on them
150 # todo: go over this list, and apply the following:
151 # remove the hash from all_song_hashes and:
152 # if I have a score on it, add it to writeout
153 # if someone else has the score on it add it to writeout with NODATA
154 # if it is in null nodes (need to write code for this) add it to writeout with NODATA
155 # all_song_hashes should now have the information we need to retrieve the song from elsewhere in stats.xml
157 writeout
="${me_highscore_name}"
158 for fuck
in "${!mmeta[@]}"; do
159 the_score
=$
(echo ${mmeta[$fuck]} | cut
-sd
: -f1
)
160 the_scorer
=$
(echo ${mmeta[$fuck]} | cut
-sd
: -f2
)
161 just_hash
=$
(echo $fuck | cut
-sd
: -f1
)
163 if [ "$the_scorer" = "$me_highscore_name" ] ||
[ "$the_scorer" = "EVNT" ]; then
164 writeout
="$writeout\n${fuck}:${the_score}"
166 if [ -z
"${nodatas["${just_hash}"]}" ]; then
167 writeout="$writeout\n${just_hash}:NODATA
"
168 nodatas["${just_hash}"]="${just_hash}"
171 unset all_song_hashes["$just_hash"]
172 #echo "$fuck $the_score $the_scorer"
175 for shit in "${null_hashes[@]}"; do
176 unset all_song_hashes["$shit"]
177 writeout="$writeout\n${shit}:NODATA
"
180 # now all that's needed is to locate the data for these nodes (they are ones that were faved this session, so cannot exist in stats.xml for the remainder of the sesh)
181 # TODO: Technically these queries are not quite specific enough. They could find scores left by players other than the machine owner
182 # but probably it's fine
183 for fuck in "${!all_song_hashes[@]}"; do
184 pathh=$(cat "${DIR}/${songs_file}" | grep $fuck | cut -sd / -f5,6 )
185 pathh=$(echo $pathh | sed 's/`/\\\`/g')
186 xmlstarlet_orphans_cmd="$xmlstarlet_orphans_cmd -i
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${pathh}/\\\"]//Steps
//HighScore
[1]//PercentDP
\" \
187 -o
\"SONG
:${fuck}\" -n \
188 -o
\"STEPTYPES
\" -n \
189 -v
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${pathh}/\\\"]//Steps
//HighScore
//PercentDP
/ancestor
::Steps
/@StepsType
\" -n \
190 -o
\"DIFFICULTIES
\" -n \
191 -v
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${pathh}/\\\"]//Steps
//HighScore
//PercentDP
/ancestor
::Steps
/@Difficulty
\" -n \
193 -v
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${pathh}/\\\"]//Steps
//HighScore
[1]//PercentDP
\" -n \
195 -o
\"SONG
:${fuck}\" -n \
200 if ! [ -z "$xmlstarlet_orphans_cmd" ]; then
201 scores_living_elsewhere=$(eval "xmlstarlet sel
-t
$xmlstarlet_orphans_cmd -o
\"SONG
\" \"${path_to_stats}\"")
202 IFS=$'\n' read -rd '' -a scores_elsewhere <<<"$scores_living_elsewhere"
206 declare -a jjj_scores
208 past_difficulties=false
211 for i in "${scores_elsewhere[@]}"; do
212 if [[ ${i:0:4} = "SONG
" ]]; then
213 if [ "$no_dump" = false ]; then
214 for j in "${!jjj_types[@]}"; do
215 writeout="${writeout}\n${jjj_hash}:${jjj_diffs[$j]}:${jjj_types[$j]}:${jjj_scores[$j]}"
218 writeout="${writeout}\n${jjj_hash}:NODATA
"
225 past_difficulties=false
228 jjj_hash=$(echo $i | cut -sd ':' -f2)
232 if [ "$i" = "NOSCORES
" ]; then
237 if [ "$i" = "STEPTYPES
" ]; then
242 if [ "$i" = "DIFFICULTIES
" ]; then
243 past_difficulties=true
247 if [ "$i" = "SCORES
" ]; then
252 if [ "$past_scores" = true ]; then
257 if [ "$past_difficulties" = true ]; then
262 if [ "$past_steptypes" = true ]; then
268 echo -e "$writeout" > "${DIR}/$me.scores.txt
"
269 scp "$DIR/$me.scores.txt
" groovenet@cameron1729.xyz:/mnt/media/GrooveNet
273 if [ "$1" = "apply
" ]; then
274 declare -A all_song_hashes
276 declare -a all_scores
280 declare -A null_files
281 xmlstarlet_deletes=""
282 xmlstarlet_orphans_cmd=""
285 if find "${DIR}"/*.favourites.txt -type f -print -quit 2>/dev/null | grep -q .; then
286 while read -d $'\0' file; do
287 IFS=$'\n' read -d '' -r -a lines < $file
288 basename=$(basename "$file")
289 person="${basename%.*.*}"
290 for i in "${lines[@]}"; do
291 all_song_hashes["$i"]+="$person"
292 pattern="${pattern}|
$i"
294 done < <(find "${DIR}"/*.favourites.txt -type f -print0)
297 pattern="${pattern:1}"
298 song_paths=$(grep -E "$pattern" "${DIR}/${songs_file}" | sort -u -k1,1)
300 while read -r song_path; do
301 hash=$(echo $song_path | cut -sd ' ' -f1)
302 path=$(echo $song_path | cut -sd / -f5-6 )
303 hash_map["$hash"]+="$path"
304 done <<< "$song_paths"
306 bros=("$me_highscore_name")
307 if find "${DIR}"/*.scores.txt -type f -print -quit 2>/dev/null | grep -q .; then
308 while read -d $'\0' file; do
309 basename=$(basename "$file")
310 person="${basename%.*.*}"
312 if [ "$person" = "$me" ]; then
313 persons_folder="$faves_folder"
315 formatted_person="$
(tr '[:lower:]' '[:upper:]' <<< ${person:0:1})${person:1}"
316 persons_folder=${external_favourite_naming_scheme/\%person\%/$formatted_person}
318 song_dir="Songs
/$persons_folder"
319 xmlstarlet_deletes="-d
\"//SongScores
//Song
[starts-with
(@Dir
, \\\"${song_dir}\\\")]\" ${xmlstarlet_deletes}"
321 IFS=$'\n' read -d '' -r -a lines < $file
322 highscorename=${lines[0]}
323 bros+=("$highscorename")
324 for i in "${lines[@]:1}"; do
325 if [ "$
(echo $i | cut
-sd
: -f2
)" = "NODATA
" ]; then
326 # null files is used to store whether or not an attempt to copy this song from its old location in stats.xml was made
327 # the way it works is:
328 # after a files score is copied over from stats.xml - it will either have a real node in stats.xml or a
329 # null placeholder one (see further down)
330 # in the push command, null nodes get written to scores.txt as hash:NODATA
331 # so the next time apply runs, we know that they had already been copied over, and we don't have to run
334 # Then later on when we process the pranks, unset any hashes that turn out to have a score elsewhere
335 # before finally adding the null nodes
336 if [ "$person" = "$me" ]; then
337 # this represents files that __should not__ be copied, that is, we know they have nothing in stats.xml at their original location
338 null_files[$(echo $i | cut -sd : -f1)]+="$person"
341 song_score=$(echo $i | rev | cut -sd : -f1 | rev)
342 song_steps_type=$(echo $i | rev | cut -sd : -f2 | rev)
343 song_difficulty=$(echo $i | rev | cut -sd : -f3 | rev)
344 song_hash=$(echo $i | rev | cut -sd : -f4 | rev)
345 scores["${highscorename}:${song_hash}:${song_difficulty}:${song_steps_type}"]+="${song_score}"
346 all_scores+=("${song_hash}:${song_difficulty}:${song_steps_type}")
349 done < <(find "${DIR}"/*.scores.txt -type f -print0)
352 # instead of using all_score_unique, use the all song hashes list
353 # that way when we come across something with no score we can build up a query for xmlstarlet
355 # for each difficulty
357 # do the bros algorithm
358 # need a flag to decide if the hash has a score,
359 # if it doesn,t add it it to the list of hashes to attempt to copy a score from
361 for hash in "${!all_song_hashes[@]}"; do
363 for d in "${difficulties[@]}"; do
364 for t in "${step_types[@]}"; do
366 for b in "${bros[@]}"; do
367 if [ -z "$highest" ]; then
370 i="${hash}:${d}:${t}"
371 if [ -z "${scores[${b}:${i}]}" ]; then
372 scores["${b}:${i}"]+="0.000000"
375 if [ -z "${scores[${highest}:${i}]}" ]; then
376 scores["${highest}:${i}"]+="0.000000"
379 if [ 1 -eq "$
(echo "${scores[${b}:${i}]} > ${scores[${highest}:${i}]}" |
bc)" ]; then
384 if ! [ "${scores[${highest}:${i}]}" = "0.000000" ]; then
385 pranks["${i}"]+="${highest}:${scores[${highest}:${i}]}"
387 # We found a score for this in someone else's scores file, a proper node will be made.
388 if ! [ -z "${null_files[$hash]}" ]; then
389 unset null_files["$hash"]
396 # TODO: Really this should happen __before__ the bros algorithm (and the unset from the bros algorithm should be removed)
397 # that way any scores I have locally for something a friend faved, will be considered. As it stands, scores from a friends
398 # scores.txt will always trump anything I have in the original directory
399 # only try find files for which there is actually nothing in scores.txt
400 if [ "$had_score" = false ] && [ -z "${null_files[${hash}]}" ]; then
401 echo "$hash had no score and is not
in nulls
"
402 song="${hash_map[${hash}]}"
403 song=$(echo $song | sed 's/`/\\\`/g')
404 xmlstarlet_orphans_cmd="$xmlstarlet_orphans_cmd -i
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${song}/\\\"]//Steps
//HighScore
[1]//PercentDP
\" \
405 -o
\"SONG
:${hash}\" -n \
406 -o
\"STEPTYPES
\" -n \
407 -v
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${song}/\\\"]//Steps
//HighScore
//PercentDP
/ancestor
::Steps
/@StepsType
\" -n \
408 -o
\"DIFFICULTIES
\" -n \
409 -v
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${song}/\\\"]//Steps
//HighScore
//PercentDP
/ancestor
::Steps
/@Difficulty
\" -n \
411 -v
\"//SongScores
//Song
[@Dir
=\\\"Songs
/${song}/\\\"]//Steps
//HighScore
[1]//PercentDP
\" -n \
413 -o
\"SONG
:${hash}\" -n \
420 scores_living_elsewhere=$(eval "xmlstarlet sel
-t
$xmlstarlet_orphans_cmd -o
\"SONG
\" \"${path_to_stats}\"")
421 IFS=$'\n' read -rd '' -a scores_elsewhere <<<"$scores_living_elsewhere"
426 declare -a jjj_scores
428 past_difficulties=false
431 for i in "${scores_elsewhere[@]}"; do
432 if [[ ${i:0:4} = "SONG
" ]]; then
433 if [ "$no_dump" = false ]; then
434 for j in "${!jjj_types[@]}"; do
435 pranks["${jjj_song}:${jjj_diffs[$j]}:${jjj_types[$j]}"]+="${me_highscore_name}:${jjj_scores[$j]}"
443 past_difficulties=false
446 jjj_song=$(echo $i | cut -sd ':' -f2)
450 if [ "$i" = "NOSCORES
" ]; then
451 faver="${all_song_hashes[${jjj_song}]}"
453 if [ "$faver" = "$me" ]; then
454 persons_folder="$faves_folder"
456 formatted_person="$
(tr '[:lower:]' '[:upper:]' <<< ${faver:0:1})${faver:1}"
457 persons_folder=${external_favourite_naming_scheme/\%person\%/$formatted_person}
460 path="$
(echo ${hash_map[${jjj_song}]} | cut
-sd
/ -f2
)"
461 path=$(echo $path | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\
"
;/g
; s
/'"'"'/\'/g')
462 song_path="Songs
/${persons_folder}/${path}/"
463 empty_nodes="${empty_nodes}\n<Song Dir
=\"$song_path\" >\
464 \n<Steps Difficulty
=\"Challenge
\" StepsType
=\"dance-single
\">\
466 \n<LastPlayed
>1933-01-02</LastPlayed
>\
467 \n<NumTimesPlayed
>1</NumTimesPlayed
>\
475 if [ "$i" = "STEPTYPES
" ]; then
480 if [ "$i" = "DIFFICULTIES
" ]; then
481 past_difficulties=true
485 if [ "$i" = "SCORES
" ]; then
490 if [ "$past_scores" = true ]; then
495 if [ "$past_difficulties" = true ]; then
500 if [ "$past_steptypes" = true ]; then
506 declare -A processed_pranks
508 for j in "${!pranks[@]}"; do
509 if [ -z "${processed_pranks[$j]}" ]; then
510 echo "$j :: ${pranks[$j]}"
511 song_pranker=$(echo "${pranks[$j]}" | cut -sd : -f1)
512 song_score=$(echo "${pranks[$j]}" | cut -sd : -f2)
513 song_steps_type=$(echo $j | cut -sd : -f3)
514 song_difficulty=$(echo $j | cut -sd : -f2)
515 song_faver=${all_song_hashes[$(echo $j | cut -sd : -f1)]}
516 song_hash=$(echo $j | cut -sd : -f1)
518 for tier in "${tiers[@]}"; do
519 if [ 1 -eq "$
(echo "$song_score < ${tier}" |
bc)" ]; then
520 song_tier=$((song_tier+1))
523 song_tier=$(printf "Tier
%02d
" $song_tier)
525 if [ "$song_faver" = "$me" ]; then
526 persons_folder="$faves_folder"
528 formatted_person="$
(tr '[:lower:]' '[:upper:]' <<< ${song_faver:0:1})${song_faver:1}"
529 persons_folder=${external_favourite_naming_scheme/\%person\%/$formatted_person}
532 song=$(echo ${hash_map[$song_hash]} | cut -sd / -f2)
533 song_dir="Songs
/${persons_folder}/${song}/"
534 song_dir=$(echo $song_dir | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\
"
;/g
; s
/'"'"'/\'/g')
535 new_nodes="$new_nodes\n<Song Dir
=\"$song_dir\" >\
536 \n<Steps Difficulty
=\"$song_difficulty\" StepsType
=\"$song_steps_type\" >\
539 \n<Name
>$song_pranker</Name
>\
540 \n<Grade
>$song_tier</Grade
>\
541 \n<PercentDP
>$song_score</PercentDP
>\
542 \n<Score
>42069</Score
>\
544 \n<LastPlayed
>2019-03-10</LastPlayed
>\
545 \n<NumTimesPlayed
>1</NumTimesPlayed
>\
549 processed_pranks[$j]+="pranked
"
551 echo "Song
: $song_dir"
552 echo "Difficulty
: $song_difficulty"
553 echo "Steps
type : $song_steps_type"
554 echo "Pranker
: $song_pranker"
555 echo "Score
: $song_score"
556 echo "Tier
: $song_tier"
559 for d in "${difficulties[@]}"; do
560 for t in "${step_types[@]}"; do
561 # there are pranks for other difficulties of this chart
562 # we need to process them now because they need to live in the
564 if ! [ -z "${pranks[${song_hash}:${d}:${t}]}" ]; then
565 if ! [ "$d" = "$song_difficulty" ] || ! [ "$t" = "$song_steps_type" ]; then
566 whatever="${pranks[${song_hash}:${d}:${t}]}"
567 ssong_pranker=$(echo $whatever | cut -sd : -f1)
568 ssong_score=$(echo $whatever | cut -sd : -f2)
570 for tier in "${tiers[@]}"; do
571 if [ 1 -eq "$
(echo "$ssong_score < ${tier}" |
bc)" ]; then
572 ssong_tier=$((ssong_tier+1))
575 ssong_tier=$(printf "Tier
%02d
" $ssong_tier)
577 processed_pranks["${song_hash}:${d}:${t}"]+="pranked
"
578 new_nodes="$new_nodes\n<Steps Difficulty
=\"${d}\" StepsType
=\"${t}\" >\
581 \n<Name
>$ssong_pranker</Name
>\
582 \n<Grade
>$ssong_tier</Grade
>\
583 \n<PercentDP
>$ssong_score</PercentDP
>\
584 \n<Score
>42069</Score
>\
586 \n<LastPlayed
>2019-03-10</LastPlayed
>\
587 \n<NumTimesPlayed
>1</NumTimesPlayed
>\
591 echo "Song
: $song_dir"
592 echo "Difficulty
: $d"
593 echo "Steps
type : $t"
594 echo "Pranker
: $ssong_pranker"
595 echo "Score
: $ssong_score"
596 echo "Tier
: $ssong_tier"
603 new_nodes="$new_nodes\n</Song
>"
607 for nully in "${!null_files[@]}"; do
608 faver="${all_song_hashes[${nully}]}"
610 if [ "$faver" = "$me" ]; then
611 persons_folder="$faves_folder"
613 formatted_person="$
(tr '[:lower:]' '[:upper:]' <<< ${faver:0:1})${faver:1}"
614 persons_folder=${external_favourite_naming_scheme/\%person\%/$formatted_person}
617 path="$
(echo ${hash_map[${nully}]} | cut
-sd
/ -f2
)"
618 path=$(echo $path | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\
"
;/g
; s
/'"'"'/\'/g')
619 song_path="Songs
/${persons_folder}/${path}/"
621 empty_nodes="${empty_nodes}\n<Song Dir
=\"$song_path\" >\
622 \n<Steps Difficulty
=\"Challenge
\" StepsType
=\"dance-single
\">\
624 \n<LastPlayed
>1933-01-02</LastPlayed
>\
625 \n<NumTimesPlayed
>1</NumTimesPlayed
>\
631 # blow away all existing nodes in the favourites folders, we will make them ourselves and append to the end of the file
632 cmd="xmlstarlet ed
-L
${xmlstarlet_deletes} \"${path_to_stats}\""
635 last_tags_removed=$(head -n -2 "${path_to_stats}")
636 echo -e "${last_tags_removed}${new_nodes}\n${empty_nodes}\n</SongScores
>\n</Stats
>" > "${path_to_stats}"
639 sed -i '1s/.*/<?xml version="1.0" encoding="UTF-8
" ?>/' "${path_to_stats}"