diff --git a/nostalgia@asphyxia/README.md b/nostalgia@asphyxia/README.md
new file mode 100644
index 0000000..727d427
--- /dev/null
+++ b/nostalgia@asphyxia/README.md
@@ -0,0 +1,14 @@
+# Nostalgia
+
+Plugin Version: **v1.0.0**
+
+Supported Versions
+-------------------
+- Op.2
+
+Changelog
+=========
+1.0.0
+-----
+Initial Release.
+
diff --git a/nostalgia@asphyxia/data/course.xml b/nostalgia@asphyxia/data/course.xml
new file mode 100644
index 0000000..4d44eb3
--- /dev/null
+++ b/nostalgia@asphyxia/data/course.xml
@@ -0,0 +1,699 @@
+
+
+
+ ベーシック10級検定
+ 40
+ 1000
+ 1
+ 80000
+
+
+ 140
+ 0
+ 850000
+
+
+ 225
+ 0
+ 1725000
+
+
+ 170
+ 0
+ 2625000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック9級検定
+ 39
+ 1500
+ 1
+ 120000
+
+
+ 199
+ 0
+ 875000
+
+
+ 209
+ 0
+ 1775000
+
+
+ 212
+ 0
+ 2700000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック8級検定
+ 38
+ 2000
+ 1
+ 200000
+
+
+ 220
+ 1
+ 900000
+
+
+ 232
+ 1
+ 1825000
+
+
+ 82
+ 0
+ 2775000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック7級検定
+ 37
+ 2500
+ 1
+ 300000
+
+
+ 186
+ 1
+ 925000
+
+
+ 141
+ 1
+ 1875000
+
+
+ 32
+ 1
+ 2850000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック6級検定
+ 36
+ 3000
+ 1
+ 400000
+
+
+ 36
+ 1
+ 925000
+
+
+ 87
+ 1
+ 1875000
+
+
+ 242
+ 1
+ 2850000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック5級検定
+ 35
+ 3500
+ 1
+ 450000
+
+
+ 122
+ 2
+ 925000
+
+
+ 40
+ 2
+ 1875000
+
+
+ 85
+ 2
+ 2850000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック4級検定
+ 34
+ 4000
+ 1
+ 500000
+
+
+ 7
+ 2
+ 925000
+
+
+ 28
+ 2
+ 1875000
+
+
+ 49
+ 0
+ 2850000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック3級検定
+ 33
+ 4500
+ 1
+ 525000
+
+
+ 8
+ 2
+ 925000
+
+
+ 29
+ 0
+ 1875000
+
+
+ 50
+ 1
+ 2850000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック2級検定
+ 32
+ 5000
+ 1
+ 540000
+
+
+ 9
+ 0
+ 925000
+
+
+ 30
+ 1
+ 1875000
+
+
+ 51
+ 2
+ 2850000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ ベーシック1級検定
+ 31
+ 6000
+ 1
+ 550000
+
+
+ 10
+ 1
+ 925000
+
+
+ 31
+ 2
+ 1875000
+
+
+ 52
+ 2
+ 2850000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル10級検定
+ 30
+ 1000
+ 2
+ 80000
+
+
+ 7
+ 1
+ 34000
+
+
+ 238
+ 1
+ 72000
+
+
+ 227
+ 1
+ 114000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル9級検定
+ 29
+ 1500
+ 2
+ 120000
+
+
+ 19
+ 1
+ 36000
+
+
+ 220
+ 1
+ 76000
+
+
+ 214
+ 1
+ 120000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル8級検定
+ 28
+ 2000
+ 2
+ 200000
+
+
+ 8
+ 1
+ 38000
+
+
+ 63
+ 1
+ 80000
+
+
+ 86
+ 2
+ 126000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル7級検定
+ 27
+ 2500
+ 2
+ 300000
+
+
+ 72
+ 2
+ 40000
+
+
+ 71
+ 2
+ 84000
+
+
+ 70
+ 2
+ 132000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル6級検定
+ 26
+ 3000
+ 2
+ 400000
+
+
+ 105
+ 1
+ 40000
+
+
+ 101
+ 2
+ 84000
+
+
+ 186
+ 2
+ 132000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル5級検定
+ 25
+ 3500
+ 2
+ 450000
+
+
+ 26
+ 2
+ 40000
+
+
+ 160
+ 1
+ 84000
+
+
+ 133
+ 2
+ 132000
+
+
+ 0
+
+ 2019-01-01 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル4級検定
+ 24
+ 4000
+ 2
+ 500000
+
+
+ 17
+ 0
+ 40000
+
+
+ 38
+ 1
+ 84000
+
+
+ 59
+ 2
+ 132000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル3級検定
+ 23
+ 4500
+ 2
+ 525000
+
+
+ 18
+ 1
+ 40000
+
+
+ 39
+ 2
+ 84000
+
+
+ 60
+ 2
+ 132000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル2級検定
+ 22
+ 5000
+ 2
+ 540000
+
+
+ 19
+ 2
+ 40000
+
+
+ 40
+ 2
+ 84000
+
+
+ 61
+ 0
+ 132000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ リサイタル1級検定
+ 21
+ 6000
+ 2
+ 550000
+
+
+ 20
+ 2
+ 40000
+
+
+ 41
+ 0
+ 84000
+
+
+ 62
+ 1
+ 132000
+
+
+ 0
+
+ 2020-01-21 10:00
+ 9999-01-01 00:00
+
+
+ KACスペシャル検定(Normal)
+ 60
+ 2000
+ 1
+ 0
+
+
+ 261
+ 0
+ 900000
+
+
+ 262
+ 0
+ 1800000
+
+
+ 263
+ 0
+ 2750000
+
+
+ 263
+
+
+ 1
+ 263
+ 2
+
+
+ 2019-02-28 10:00
+ 9999-01-01 00:00
+
+
+ KACスペシャル検定(Hard)
+ 59
+ 2000
+ 1
+ 0
+
+
+ 261
+ 1
+ 925000
+
+
+ 262
+ 1
+ 1875000
+
+
+ 263
+ 1
+ 2850000
+
+
+ 263
+
+
+ 1
+ 263
+ 2
+
+
+ 2019-02-28 10:00
+ 9999-01-01 00:00
+
+
+ KACスペシャル検定(Expert)
+ 58
+ 2000
+ 1
+ 0
+
+
+ 261
+ 2
+ 925000
+
+
+ 262
+ 2
+ 1875000
+
+
+ 263
+ 2
+ 2850000
+
+
+ 263
+
+
+ 1
+ 263
+ 2
+
+
+ 2019-02-28 10:00
+ 9999-01-01 00:00
+
+
+ KACスペシャル検定(Real)
+ 57
+ 2000
+ 1
+ 0
+
+
+ 261
+ 3
+ 925000
+
+
+ 262
+ 3
+ 1875000
+
+
+ 263
+ 3
+ 2850000
+
+
+ 263
+
+
+ 1
+ 263
+ 2
+
+
+ 2019-02-28 10:00
+ 9999-01-01 00:00
+
+
\ No newline at end of file
diff --git a/nostalgia@asphyxia/data/island.xml b/nostalgia@asphyxia/data/island.xml
new file mode 100644
index 0000000..b4c558b
--- /dev/null
+++ b/nostalgia@asphyxia/data/island.xml
@@ -0,0 +1,1996 @@
+
+
+
+ ひとりぼっちの世界
+ 1
+ 0
+
+ 1
+ 0
+
+ 0
+
+
+
+ 1
+ 1
+ 0
+ _label
+
+ 0
+ 0
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ 1
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ はじまりの街
+ 2
+ 0
+
+ 1
+ 1
+
+ 0
+
+
+
+ 1
+ 2
+ 1
+ _label
+
+ 214
+ 15
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 1
+
+
+ 1
+ 2
+
+
+ 3
+ 4
+ 5
+ 32
+ 35
+
+ 1
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 旅立ちの海
+ 2
+ 500
+
+ 1
+ 4
+
+ 214
+
+
+
+ 1
+ 3
+ 2
+ _label
+
+ 215
+ 36
+
+
+ 216
+ 54
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 2
+
+
+ 3
+ 4
+
+
+ 2
+ 5
+ 16
+ 15
+ 32
+ 35
+
+ 0
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 砂の国
+ 2
+ 2500
+
+ 1
+ 9
+
+ 216
+
+
+
+ 1
+ 4
+ 3
+ _label
+
+ 217
+ 54
+
+
+ 218
+ 72
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 3
+ 4
+
+
+ 5
+ 6
+ 7
+
+
+ 1
+ 6
+ 7
+ 15
+ 14
+ 32
+ 35
+
+ 0
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 小さな家
+ 2
+ 5000
+
+ 1
+ 10
+
+ 218
+
+
+
+ 1
+ 5
+ 4
+ _label
+
+ 219
+ 152
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 5
+ 6
+
+
+ 8
+ 9
+ 10
+
+
+ 1
+ 7
+ 8
+ 14
+ 13
+ 32
+ 35
+
+ 0
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 目覚めの森
+ 3
+ 0
+
+ 1
+ 3
+
+ 0
+
+
+
+ 1
+ 6
+ 5
+ _label
+
+ 220
+ 15
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 7
+
+
+ 11
+ 12
+
+
+ 3
+ 4
+ 5
+ 32
+ 35
+
+ 1
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 風の丘
+ 3
+ 500
+
+ 1
+ 8
+
+ 220
+
+
+
+ 1
+ 7
+ 6
+ _label
+
+ 221
+ 36
+
+
+ 211
+ 54
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 8
+
+
+ 13
+ 14
+
+
+ 2
+ 5
+ 17
+ 23
+ 32
+ 35
+
+ 0
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 水晶の谷
+ 3
+ 2500
+
+ 1
+ 13
+
+ 211
+
+
+
+ 1
+ 8
+ 7
+ _label
+
+ 222
+ 54
+
+
+ 223
+ 72
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 9
+ 10
+
+
+ 15
+ 16
+ 17
+
+
+ 1
+ 6
+ 7
+ 19
+ 25
+ 32
+ 35
+
+ 0
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ 小さな家
+ 3
+ 5000
+
+ 1
+ 12
+
+ 223
+
+
+
+ 1
+ 9
+ 8
+ _label
+
+ 224
+ 152
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 11
+ 12
+
+
+ 18
+ 19
+ 20
+
+
+ 1
+ 7
+ 8
+ 21
+ 26
+ 32
+ 35
+
+ 0
+ 2018-01-01 05:00
+ 9999-12-31 23:59
+
+
+ Early Summer Island
+ 1
+ 500
+
+ 1
+ 1
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 188
+ 36
+
+
+ 191
+ 54
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 13
+
+
+ 21
+
+
+ 2
+ 5
+ 24
+ 18
+ 32
+ 35
+
+ 0
+ 2019-08-01 10:00
+ 2019-08-15 05:00
+
+
+ Mid-Summer Island
+ 1
+ 500
+
+ 1
+ 2
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 192
+ 36
+
+
+ 190
+ 54
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 14
+
+
+ 22
+
+
+ 2
+ 5
+ 24
+ 18
+ 32
+ 35
+
+ 0
+ 2019-08-08 10:00
+ 2019-08-22 05:00
+
+
+ Post-Summer Island
+ 1
+ 1000
+
+ 1
+ 3
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 189
+ 48
+
+
+ 193
+ 54
+
+
+ 194
+ 24
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 15
+
+
+ 23
+
+
+ 1
+ 6
+ 7
+ 25
+ 19
+ 32
+ 35
+
+ 0
+ 2019-08-15 10:00
+ 2019-08-29 05:00
+
+
+ 実りの里-やさい畑-
+ 1
+ 2000
+
+ 1
+ 10
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 244
+ 54
+
+
+ 241
+ 72
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 17
+
+
+ 25
+
+
+ 1
+ 8
+ 9
+ 21
+ 26
+ 32
+ 35
+
+ 0
+ 2019-03-14 10:00
+ 2019-03-28 05:00
+
+
+ 実りの里-くだもの畑-
+ 1
+ 2000
+
+ 1
+ 11
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 243
+ 54
+
+
+ 242
+ 72
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 18
+
+
+ 26
+
+
+ 1
+ 8
+ 9
+ 15
+ 14
+ 32
+ 35
+
+ 0
+ 2019-03-21 10:00
+ 2019-04-04 05:00
+
+
+ KACリサイタル部門
+ 1
+ 0
+
+ 1
+ 7
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 0
+ 0
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 16
+
+
+ 24
+
+
+ 0
+
+ 0
+ 2018-11-21 10:00
+ 2019-01-07 18:00
+
+
+ 忘れられた廃棄場
+ 4
+ 3000
+
+ 1
+ 2
+
+ 214
+ 220
+
+
+
+ 1
+ 10
+ 9
+ _label
+
+ 250
+ 30
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 19
+
+
+ 27
+
+
+ 1
+ 4
+ 5
+ 25
+ 26
+ 32
+ 35
+
+ 0
+ 2019-01-10 10:00
+ 9999-12-31 23:59
+
+
+ おもちゃの王国
+ 4
+ 5000
+
+ 1
+ 6
+
+ 250
+
+
+
+ 1
+ 11
+ 10
+ _label
+
+ 251
+ 52
+
+
+ 252
+ 76
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 20
+ 21
+
+
+ 28
+ 29
+
+
+ 1
+ 7
+ 8
+ 21
+ 26
+ 32
+ 35
+
+ 0
+ 2019-01-10 10:00
+ 9999-12-31 23:59
+
+
+ 真夜中の湖畔
+ 3
+ 7500
+
+ 2
+ 3
+
+ 219
+ 224
+ 252
+
+
+
+ 1
+ 12
+ 11
+ _label
+
+ 254
+ 124
+
+
+ 253
+ 54
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 22
+ 23
+
+
+ 30
+ 31
+
+
+ 1
+ 8
+ 9
+ 14
+ 13
+ 32
+ 35
+
+ 0
+ 2019-01-10 10:00
+ 9999-12-31 23:59
+
+
+ 郷愁の階段
+ 1
+ 1000
+
+ 1
+ 6
+
+ 0
+
+
+
+ 3
+ 1
+ 0
+ _label
+
+ 19
+ 12
+
+
+ 38
+ 18
+
+
+ 3
+ 24
+
+
+ 20
+ 24
+
+
+ 41
+ 30
+
+
+ 34
+ 30
+
+
+ 42
+ 36
+
+
+ 31
+ 36
+
+
+ 35
+ 36
+
+
+ 43
+ 36
+
+
+ 33
+ 42
+
+
+ 73
+ 42
+
+
+ 74
+ 48
+
+
+ 75
+ 48
+
+
+ 81
+ 48
+
+
+ 82
+ 48
+
+
+ 98
+ 48
+
+
+ 99
+ 48
+
+
+ 134
+ 48
+
+
+ 135
+ 52
+
+
+ 153
+ 52
+
+
+ 181
+ 52
+
+
+ 182
+ 52
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 24
+ 25
+
+
+ 32
+ 33
+ 34
+
+
+ 1
+ 7
+ 16
+ 20
+ 25
+ 29
+ 33
+ 35
+
+ 0
+ 2018-12-25 10:00
+ 9999-12-31 23:59
+
+
+ 凍てつく山脈
+ 1
+ 3000
+
+ 1
+ 12
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 140
+ 16
+
+
+ 141
+ 16
+
+
+ 142
+ 16
+
+
+ 247
+ 24
+
+
+ 248
+ 54
+
+
+ 249
+ 72
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 26
+
+
+ 35
+
+
+ 2
+ 6
+ 25
+ 19
+ 32
+ 35
+
+ 0
+ 2019-04-25 10:00
+ 2019-05-09 05:00
+
+
+ 魅惑の黄金パレス
+ 1
+ 500
+
+ 1
+ 5
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 0
+ 0
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 27
+
+
+ 36
+ 37
+ 38
+
+
+ 0
+
+ 0
+ 2019-07-25 10:00
+ 2019-08-08 05:00
+
+
+ 幻想郷を彩る階段
+ 1
+ 1000
+
+ 1
+ 9
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 178
+ 20
+
+
+ 179
+ 30
+
+
+ 177
+ 42
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 28
+
+
+ 39
+
+
+ 1
+ 5
+ 6
+ 16
+ 15
+ 29
+ 33
+ 35
+
+ 0
+ 2019-02-07 10:00
+ 9999-12-31 23:59
+
+
+ 風がそよぐ階段
+ 1
+ 2000
+
+ 1
+ 4
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 87
+ 24
+
+
+ 170
+ 32
+
+
+ 171
+ 40
+
+
+ 172
+ 54
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 29
+
+
+ 40
+
+
+ 1
+ 5
+ 6
+ 24
+ 25
+ 29
+ 33
+ 35
+
+ 0
+ 2019-02-07 10:00
+ 9999-12-31 23:59
+
+
+ 秋雪が舞う階段
+ 1
+ 2000
+
+ 1
+ 13
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 131
+ 24
+
+
+ 147
+ 32
+
+
+ 148
+ 42
+
+
+ 149
+ 50
+
+
+ 130
+ 64
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 30
+
+
+ 41
+
+
+ 1
+ 5
+ 6
+ 18
+ 19
+ 29
+ 33
+ 35
+
+ 0
+ 2019-02-07 10:00
+ 9999-12-31 23:59
+
+
+ 光が降り注ぐ階段
+ 1
+ 3000
+
+ 1
+ 8
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 76
+ 32
+
+
+ 105
+ 48
+
+
+ 160
+ 64
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 31
+
+
+ 42
+
+
+ 1
+ 7
+ 8
+ 22
+ 26
+ 29
+ 33
+ 35
+
+ 0
+ 2019-02-07 10:00
+ 9999-12-31 23:59
+
+
+ アニバーサリーアイランド
+ 1
+ 500
+
+ 1
+ 7
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 270
+ 48
+
+
+ 280
+ 48
+
+
+ 279
+ 54
+
+
+ 269
+ 72
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 33
+
+
+ 43
+
+
+ 1
+ 5
+ 24
+ 19
+ 16
+ 32
+ 35
+
+ 0
+ 2019-03-21 10:00
+ 9999-12-31 23:59
+
+
+ 化石の砦
+ 5
+ 3500
+
+ 2
+ 6
+
+ 214
+ 220
+
+
+
+ 1
+ 13
+ 12
+ _label
+
+ 264
+ 45
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 34
+
+
+ 44
+
+
+ 1
+ 4
+ 5
+ 25
+ 26
+ 32
+ 35
+
+ 0
+ 2019-04-11 10:00
+ 9999-12-31 23:59
+
+
+ 雲のエアポート
+ 5
+ 7000
+
+ 2
+ 11
+
+ 264
+
+
+
+ 1
+ 14
+ 13
+ _label
+
+ 265
+ 52
+
+
+ 266
+ 76
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 35
+
+
+ 45
+ 46
+
+
+ 1
+ 7
+ 8
+ 21
+ 26
+ 32
+ 35
+
+ 0
+ 2019-04-11 10:00
+ 9999-12-31 23:59
+
+
+ 試練の活火山
+ 5
+ 9500
+
+ 2
+ 10
+
+ 266
+
+
+
+ 1
+ 15
+ 14
+ _label
+
+ 285
+ 84
+
+
+ 286
+ 100
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 36
+
+
+ 47
+
+
+ 1
+ 8
+ 9
+ 14
+ 26
+ 32
+ 35
+
+ 0
+ 2019-05-23 10:00
+ 9999-12-31 23:59
+
+
+ 白昼夢の水平線
+ 1
+ 3000
+
+ 2
+ 2
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 287
+ 54
+
+
+ 284
+ 73
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 37
+
+
+ 48
+ 49
+
+
+ 1
+ 7
+ 9
+ 21
+ 14
+ 32
+ 35
+
+ 0
+ 2019-07-25 10:00
+ 2019-08-08 05:00
+
+
+ 雨の街
+ 2
+ 7500
+
+ 2
+ 1
+
+ 219
+ 224
+ 252
+
+
+
+ 1
+ 16
+ 15
+ _label
+
+ 291
+ 54
+
+
+ 292
+ 124
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 38
+ 39
+
+
+ 50
+ 51
+
+
+ 1
+ 7
+ 9
+ 21
+ 26
+ 32
+ 35
+
+ 0
+ 2019-07-11 10:00
+ 9999-12-31 23:59
+
+
+ Keyboard Mythology Island
+ 1
+ 2000
+
+ 2
+ 1
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 288
+ 36
+
+
+ 289
+ 92
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 40
+
+
+ 52
+
+
+ 1
+ 7
+ 8
+ 12
+ 27
+ 32
+ 35
+
+ 0
+ 2019-08-22 10:00
+ 2019-09-05 05:00
+
+
+ GUMI 10th Anniversary Island
+ 1
+ 500
+
+ 2
+ 3
+
+ 0
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 293
+ 30
+
+
+ 294
+ 50
+
+
+ 295
+ 76
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 41
+
+
+ 53
+
+
+ 1
+ 6
+ 7
+ 16
+ 29
+ 32
+ 35
+
+ 0
+ 2019-06-26 10:00
+ 2019-07-11 05:00
+
+
+ 眠らない工場
+ 4
+ 6500
+
+ 2
+ 12
+
+ 252
+
+
+
+ 1
+ 17
+ 16
+ _label
+
+ 296
+ 54
+
+
+ 297
+ 124
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 42
+ 45
+
+
+ 54
+ 57
+
+
+ 1
+ 8
+ 9
+ 12
+ 27
+ 32
+ 35
+
+ 0
+ 2019-08-08 10:00
+ 9999-12-31 23:59
+
+
+ 最果ての研究所
+ 1
+ 5000
+
+ 3
+ 2
+
+ 253
+ 286
+ 292
+ 297
+
+
+
+ 1
+ 18
+ 0
+ _label
+
+ 267
+ 130
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 43
+ 46
+
+
+ 55
+ 58
+
+
+ 1
+ 6
+ 14
+ 22
+ 26
+ 32
+ 35
+
+ 0
+ 2019-08-22 10:00
+ 9999-12-31 23:59
+
+
+ はじまりの場所
+ 1
+ 4500
+
+ 3
+ 6
+
+ 267
+
+
+
+ 1
+ 19
+ 0
+ _label
+
+ 213
+ 50
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 44
+
+
+ 56
+
+
+ 1
+ 7
+ 13
+ 42
+ 26
+ 32
+ 35
+
+ 0
+ 2019-08-22 10:00
+ 9999-12-31 23:59
+
+
+ 記憶の終着駅
+ 1
+ 10000
+
+ 3
+ 11
+
+ 213
+
+
+
+ 1
+ 20
+ 0
+ _label
+
+ 298
+ 100
+
+
+
+ 1
+ 0
+ 0
+ _label
+
+ 299
+ 0
+
+
+
+ 1
+ 0
+ 0
+ _label
+
+ 301
+ 76
+
+
+
+ 1
+ 0
+ 0
+ _label
+
+ 300
+ 76
+
+
+
+ 1
+ 0
+ 0
+ _label
+
+ 302
+ 76
+
+
+
+ 1
+ 0
+ 0
+ _label
+
+ 303
+ 76
+
+
+
+ 3
+ 0
+ 0
+ _label
+
+ 305
+ 85
+
+
+
+ 1
+ 1
+ 0
+ 1
+
+ 47
+ 48
+ 49
+
+
+ 59
+ 60
+ 61
+
+
+ 1
+ 8
+ 12
+ 43
+ 29
+ 32
+ 35
+
+ 0
+ 2019-09-26 10:00
+ 9999-12-31 23:59
+
+
\ No newline at end of file
diff --git a/nostalgia@asphyxia/handler/common.ts b/nostalgia@asphyxia/handler/common.ts
new file mode 100644
index 0000000..ad45e96
--- /dev/null
+++ b/nostalgia@asphyxia/handler/common.ts
@@ -0,0 +1,114 @@
+import * as path from "path";
+
+export const permitted_list = {
+ flag: [
+ K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '0' }),
+ K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '1' }),
+ K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '2' }),
+ K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '3' }),
+ ],
+};
+
+async function ReadData(filename: string) {
+ const xml = await IO.ReadFile(`data/${filename}.xml`, { encoding: 'utf-8'});
+ const json = U.parseXML(xml, false)
+ return json
+}
+
+async function processIslandData() {
+ const islandData = (await ReadData('island')).island_data_list.island_data;
+ for (const island of islandData) {
+ island.flag_permitted = K.ITEM('bool', 1);
+ island.cost['@content'][0] = ~~(island.cost['@content'][0] / 10);
+ let containers = island.get_music_index_list.container;
+ if (!_.isArray(containers)) {
+ containers = [containers];
+ }
+
+ for (const cont of containers) {
+ cont['@attr'].no = cont['@attr'].container_no;
+ delete cont['@attr'].container_no;
+ if (!_.isArray(cont.music)) {
+ cont.music = [cont.music];
+ }
+
+ for (const m of cont.music) {
+ m.get_point['@content'][0] = ~~(m.get_point['@content'][0] / 6);
+ }
+ }
+
+ island.get_music_index_list.container = containers;
+ };
+
+ return { island_data: islandData };
+}
+
+async function processCourseData() {
+ const courseData = (await ReadData('course')).course_data_list.course_data;
+ for (const course of courseData) {
+ course.req_nos['@content'][0] = ~~(course.req_nos['@content'][0] / 10);
+ if (course.seq_list && course.seq_list.tune) {
+ course.seq_list = course.seq_list.tune;
+ course.is_open = K.ITEM('bool', 1);
+ }
+ }
+
+ return { course_data: courseData };
+}
+
+export const get_common_info = async (req, data, send) => {
+ send.object({
+ permitted_list,
+ olupdate: {
+ delete_flag: K.ITEM('bool', 0),
+ },
+ });
+};
+
+export const get_music_info: EPR = async (req, data, send) => {
+ const music_spec: any = [];
+ for (let i = 1; i < 400; ++i) {
+ music_spec.push(K.ATTR({ index: `${i}`}, {
+ jk_jpn: K.ITEM('bool', 1),
+ jk_asia: K.ITEM('bool', 1),
+ jk_kor: K.ITEM('bool', 1),
+ jk_idn: K.ITEM('bool', 1),
+ start_date: K.ITEM('str', '2017-01-11 10:00'),
+ end_date: K.ITEM('str', '9999-12-31 23:59'),
+ expiration_date: K.ITEM('str', '9999-12-31 23:59'),
+ real_start_date: K.ITEM('str', '2017-01-11 10:00'),
+ real_end_date: K.ITEM('str', '9999-12-31 23:59'),
+ real_once_price: K.ITEM('s32', 300),
+ real_forever_price: K.ITEM('s32', 7500),
+ }));
+ }
+
+ send.object({
+ permitted_list,
+
+ island_data_list: await processIslandData(),
+ course_data_list: await processCourseData(),
+
+ overwrite_music_list: K.ATTR({
+ revision: '16706',
+ release_code: '2019100200',
+ }, {
+ music_spec: music_spec,
+ }),
+
+ gamedata_flag_list: {
+ event: {
+ index: K.ITEM('s32', 0),
+ status: K.ITEM('s8', 1),
+ start_time: K.ITEM('u64', BigInt(0)),
+ end_time: K.ITEM('u64', BigInt(0)),
+ param1: K.ITEM('u64', BigInt(0)),
+ param2: K.ITEM('u64', BigInt(0)),
+ },
+ },
+
+ olupdate: {
+ delete_flag: K.ITEM('bool', 0),
+ },
+ });
+};
diff --git a/nostalgia@asphyxia/handler/player.ts b/nostalgia@asphyxia/handler/player.ts
new file mode 100644
index 0000000..fa4c775
--- /dev/null
+++ b/nostalgia@asphyxia/handler/player.ts
@@ -0,0 +1,553 @@
+// import { EAHandler } from '../../util/EAHandler';
+// import { get, _.isArray } from 'lodash';
+// import { Logger } from '../../util/Logger';
+import { Profile } from '../models/profile';
+import { Scores } from '../models/scores';
+import { permitted_list } from './common';
+// import { getValue, getArray, getAttr, getStr, getBigInt } from '../../util/Helper';
+
+
+// export const event_list = {
+// event: {
+// '@attr': {
+// index: 1,
+// },
+// 'status': K.ITEM('s8', 1),
+// 'start_time': K.ITEM('u64', BigInt(0)),
+// 'end_time': K.ITEM('u64', BigInt(0)),
+// },
+// };
+
+const getEventInfo = () => {
+ const event: any[] = [];
+ for (let i = 1; i <= 17; ++i) {
+ event.push({
+ type: K.ITEM('s32', 4),
+ index: K.ITEM('s32', i),
+ status: K.ITEM('s8', 1),
+ start_time: K.ITEM('u64', BigInt(0)),
+ end_time: K.ITEM('u64', BigInt(0)),
+ param1: K.ITEM('u64', BigInt(0)),
+ param2: K.ITEM('u64', BigInt(0)),
+ });
+ }
+ return event;
+};
+
+const getPlayerData = async (refid: string, name?: string) => {
+ const p = await readProfile(refid);
+
+ if (name && name.length > 0) {
+ p.name = name;
+ await writeProfile(refid, p);
+ }
+
+ const param: any[] = [];
+ for (const t in p.params) {
+ const para = p.params[t];
+ param.push(K.ATTR({type: t}, {
+ count: K.ITEM('s32', para.length),
+ params_array: K.ARRAY('s32', para),
+ }));
+ }
+
+ const brooch: any[] = [];
+ for (const b in p.brooches) {
+ const bData = p.brooches[b];
+ brooch.push(K.ATTR({ index: b }, {
+ watch_count: K.ITEM('s32', bData.watch),
+ level: K.ITEM('s8', bData.level),
+ invested_steps: K.ITEM('s32', bData.steps),
+ is_new_brooch: K.ITEM('bool', bData.new),
+ }));
+ }
+
+ // Unlock brooches
+ for (let i = 101; i <= 124; ++i) {
+ brooch.push(K.ATTR({ index: `${i}` }, {
+ 'watch_count': K.ITEM('s32', 0),
+ 'level': K.ITEM('s8', 1),
+ 'invested_steps': K.ITEM('s32', 0),
+ 'is_new_brooch': K.ITEM('bool', 0),
+ }));
+ }
+
+ const kentei_record: any[] = [];
+ for (const k in p.kentei) {
+ const kentei = p.kentei[k];
+
+ kentei_record.push({
+ stage_prog: K.ITEM('s8', kentei.stage),
+ kentei_index: K.ITEM('s32', parseInt(k, 10)),
+ score: K.ARRAY('s32', kentei.score),
+ clear_rate: K.ITEM('s32', kentei.rate),
+ clear_flag: K.ITEM('u32', kentei.flag),
+ play_count: K.ITEM('s32', kentei.count),
+ });
+ }
+
+ const island_progress: any[] = [];
+ for (const i in p.islands) {
+ const island = p.islands[i];
+
+ const container: any[] = [];
+ for (const c in island.containers) {
+ const cont = island.containers[c];
+
+ const reward: any[] = [];
+ for (const r in cont.rewards) {
+ const rew = cont.rewards[r];
+
+ reward.push({
+ reward_index: K.ITEM('s32', parseInt(r, 10)),
+ point: K.ITEM('s32', rew),
+ });
+ }
+
+ container.push({
+ container_no: K.ITEM('s32', parseInt(c, 10)),
+ movie_prog: K.ITEM('s8', cont.prog),
+ reward,
+ });
+ }
+
+ island_progress.push(K.ATTR({ index: i }, {
+ lookUnlockWin: K.ITEM('s32', island.look),
+ select_container: K.ITEM('s32', island.select),
+ travelledTime: K.ITEM('u32', island.time),
+ container,
+ }));
+ }
+
+ return {
+ name: K.ITEM('str', p.name),
+ play_count: K.ITEM('s32', p.playCount),
+ today_play_count: K.ITEM('s32', p.todayPlayCount),
+ permitted_list,
+ event_info_list: { event: getEventInfo() },
+ music_list: {
+ flag: [
+ K.ARRAY('s32', p.musicList.type_0, { sheet_type: '0' }),
+ K.ARRAY('s32', p.musicList.type_1, { sheet_type: '1' }),
+ K.ARRAY('s32', p.musicList.type_2, { sheet_type: '2' }),
+ K.ARRAY('s32', p.musicList.type_3, { sheet_type: '3' }),
+ ],
+ },
+ free_for_play_music_list: {
+ flag: [
+ K.ARRAY('s32', p.musicList2.type_0, { sheet_type: '0' }),
+ K.ARRAY('s32', p.musicList2.type_1, { sheet_type: '1' }),
+ K.ARRAY('s32', p.musicList2.type_2, { sheet_type: '2' }),
+ K.ARRAY('s32', p.musicList2.type_3, { sheet_type: '3' }),
+ ],
+ },
+ last: {
+ music_index: K.ITEM('s32', p.music),
+ sheet_type: K.ITEM('s8', p.sheet),
+ brooch_index: K.ITEM('s32', p.brooch),
+ hi_speed_level: K.ITEM('s32', p.hispeed),
+ beat_guide: K.ITEM('s8', p.beatGuide),
+ headphone_volume: K.ITEM('s8', p.headphone),
+ judge_bar_pos: K.ITEM('s32', p.judgeBar),
+ music_group: K.ITEM('s32', p.group),
+ hands_mode: K.ITEM('s8', p.mode),
+ near_setting: K.ITEM('s8', p.near),
+ judge_delay_offset: K.ITEM('s8', p.offset),
+ bingo_index: K.ITEM('s32', p.bingo),
+ total_skill_value: K.ITEM('u64', BigInt(p.skill)),
+ key_beam_level: K.ITEM('s8', p.keyBeam),
+ orbit_type: K.ITEM('s8', p.orbit),
+ note_height: K.ITEM('s8', p.noteHeight),
+ note_width: K.ITEM('s8', p.noteWidth),
+ judge_width_type: K.ITEM('s8', p.judgeWidth),
+ beat_guide_volume: K.ITEM('s8', p.beatVolume),
+ beat_guide_type: K.ITEM('s8', p.beatType),
+ key_volume_offset: K.ITEM('s8', p.keyVolume),
+ bgm_volume_offset: K.ITEM('s8', p.bgmVolume),
+ note_disp_type: K.ITEM('s8', p.note),
+ slow_fast: K.ITEM('s8', p.sf),
+ judge_effect_adjust: K.ITEM('s8', p.judgeFX),
+ simple_bg: K.ITEM('s8', p.simple),
+ },
+ brooch_list: {
+ brooch,
+ },
+ extra_param: { param },
+ present_list: {},
+ various_music_list: {
+ data: [
+ K.ATTR({ list_type: '0' }, {
+ cond_flag: K.ITEM('s32', 0),
+ flag: K.ITEM('s32', 0, { sheet_type: '0' }),
+ }),
+ ],
+ },
+ island_progress_list: { island_progress },
+ player_information_list: {},
+ kentei_record_list: { kentei_record },
+ linkage_data_list: {},
+ travel: {
+ money: K.ITEM('s32', p.money),
+ fame: K.ITEM('s32', p.fame),
+ fame_index: K.ITEM('s32', p.fameId),
+ island_id: K.ITEM('s32', p.island),
+ },
+ };
+};
+
+export const regist_playdata: EPR = async (info, data, send) => {
+ const refid = $(data).str('refid');
+ if (!refid) return send.deny();
+
+ const name = $(data).str('name');
+ console.debug(`nos op2 regist: ${name}`);
+
+ send.object(await getPlayerData(refid, name));
+};
+
+export const get_playdata: EPR = async (info, data, send) => {
+ const refid = $(data).str('refid');
+ if (!refid) return send.deny();
+
+ send.object(await getPlayerData(refid));
+};
+
+// export const set_stage_result: EPR = async (info, data, send) => {
+// return send.object();
+// };
+
+export const set_total_result: EPR = async (info, data, send) => {
+ const refid = $(data).str('refid');
+ if (!refid) return send.deny();
+
+ const p = await readProfile(refid);
+
+ p.playCount = $(data).number('play_count', p.playCount);
+ p.todayPlayCount = $(data).number('today_play_count', p.todayPlayCount);
+
+ const last = $(data).element('last');
+
+ p.music = last.number('music_index', p.music);
+ p.sheet = last.number('sheet_type', p.sheet);
+ p.brooch = last.number('brooch_index', p.brooch);
+ p.hispeed = last.number('hi_speed_level', p.hispeed);
+ p.beatGuide = last.number('beat_guide', p.beatGuide);
+ p.headphone = last.number('headphone_volume', p.headphone);
+ p.judgeBar = last.number('judge_bar_pos', p.judgeBar);
+ p.group = last.number('music_group', p.group);
+ p.mode = last.number('hands_mode', p.mode);
+ p.near = last.number('near_setting', p.near);
+ p.offset = last.number('judge_delay_offset', p.offset);
+ p.bingo = last.number('bingo_index', p.bingo);
+ p.skill = `${last.bigint('total_skill_value') || p.skill}`;
+ p.keyBeam = last.number('key_beam_level', p.keyBeam);
+ p.orbit = last.number('orbit_type', p.orbit);
+ p.noteHeight = last.number('note_height', p.noteHeight);
+ p.noteWidth = last.number('note_width', p.noteWidth);
+ p.judgeWidth = last.number('judge_width_type', p.judgeWidth);
+ p.beatVolume = last.number('beat_guide_volume', p.beatVolume);
+ p.beatType = last.number('beat_guide_type', p.beatType);
+ p.keyVolume = last.number('key_volume_offset', p.keyVolume);
+ p.bgmVolume = last.number('bgm_volume_offset', p.bgmVolume);
+ p.note = last.number('note_disp_type', p.note);
+ p.sf = last.number('slow_fast', p.sf);
+ p.judgeFX = last.number('judge_effect_adjust', p.judgeFX);
+ p.simple = last.number('simple_bg', p.simple);
+
+ p.money = $(data).number('travel.money', p.money);
+ p.fame = $(data).number('travel.fame', p.fame);
+ p.fameId = $(data).number('travel.fame_index', p.fameId);
+ p.island = $(data).number('travel.island_id', p.island);
+
+ let flags = _.get($(data).obj, 'music_list.flag', []);
+ if (!_.isArray(flags)) flags = [flags];
+ for (const flag of flags) {
+ const sheet = _.get(flag, '@attr.sheet_type', -1);
+ if (sheet == '0') {
+ p.musicList.type_0 = _.get(flag, '@content', p.musicList.type_0);
+ } else if (sheet == '1') {
+ p.musicList.type_1 = _.get(flag, '@content', p.musicList.type_1);
+ } else if (sheet == '2') {
+ p.musicList.type_2 = _.get(flag, '@content', p.musicList.type_2);
+ } else if (sheet == '3') {
+ p.musicList.type_3 = _.get(flag, '@content', p.musicList.type_3);
+ }
+ }
+
+ let freeFlags = _.get($(data).obj, 'free_for_play_music_list.flag', []);
+ if (!_.isArray(freeFlags)) freeFlags = [freeFlags];
+ for (const flag of freeFlags) {
+ const sheet = _.get(flag, '@attr.sheet_type', -1);
+ if (sheet == '0') {
+ p.musicList2.type_0 = _.get(flag, '@content', p.musicList2.type_0);
+ } else if (sheet == '1') {
+ p.musicList2.type_1 = _.get(flag, '@content', p.musicList2.type_1);
+ } else if (sheet == '2') {
+ p.musicList2.type_2 = _.get(flag, '@content', p.musicList2.type_2);
+ } else if (sheet == '3') {
+ p.musicList2.type_3 = _.get(flag, '@content', p.musicList2.type_3);
+ }
+ }
+
+ // KENTEI
+ let kenteis = $(data).elements('kentei_result_list.kentei_result');
+ for (const kentei of kenteis) {
+ const index = kentei.number('kentei_index', -1);
+ if (index < 0) continue;
+
+ const clearRate = kentei.number('clear_rate', 0);
+ const oldClearRate = _.get(p, `kentei.${index}.rate`, 0);
+ const isHigh = clearRate >= oldClearRate;
+
+ p.kentei[index] = {
+ rate: isHigh ? clearRate : oldClearRate,
+ score: isHigh ? kentei.number('score', 0) : _.get(p, `kentei.${index}.score`, 0),
+ stage: Math.max(kentei.number('stage_prog', 0), _.get(p, `kentei.${index}.stage`, 0)),
+ flag: Math.max(kentei.number('clear_flag', 0), _.get(p, `kentei.${index}.flag`, 0)),
+ count: Math.max(kentei.number('play_count', 0), _.get(p, `kentei.${index}.count`, 0)),
+ };
+ }
+
+ // PARAMS
+ let params = $(data).elements('extra_param.param');
+ for (const param of params) {
+ const type = param.attr().type;
+ const parray = param.numbers('params_array');
+ if (type == null || parray == null) continue;
+
+ p.params[type] = parray;
+ }
+
+ // BROOCHES
+ let broochs = $(data).elements('brooch_list.brooch');
+ for (const brooch of broochs) {
+ const index = parseInt(_.get(brooch, '@attr.index', '-1'));
+ if (index < 0) continue;
+
+ p.brooches[index] = {
+ watch: brooch.number('watch_count', 0),
+ level: brooch.number('level', 1),
+ steps: brooch.number('invested_steps', 0),
+ new: brooch.number('is_new_brooch', 0),
+ };
+ }
+
+ // ISLAND
+ let islands = $(data).elements('island_progress_list.island_progress');
+ for (const island of islands) {
+ const index = parseInt(_.get(island, '@attr.index', '-1'));
+ if (index < 0) continue;
+
+ const containers: Profile['islands']['0']['containers'] = {};
+ let conts = $(data).elements('container');
+ for (const cont of conts) {
+ const index = cont.number('container_no', -1);
+ if (index < 0) continue;
+
+ const rewards: { [key: string]: number } = {};
+ let rews = $(data).elements('reward');
+ for (const rew of rews) {
+ const index = rew.number('reward_index', -1);
+ if (index < 0) continue;
+ rewards[index] = rew.number('point', 0);
+ }
+
+ containers[index] = {
+ prog: cont.number('movie_prog', 1),
+ rewards,
+ };
+ }
+
+ p.islands[index] = {
+ look: island.number('lookUnlockWin', 0),
+ select: island.number('select_container', 0),
+ time: island.number('travelledTime', 0),
+ containers,
+ };
+ }
+
+ await writeProfile(refid, p);
+
+ const scoreData = await readScores(refid);
+ // Save Scores
+ let stages = $(data).elements('stageinfo.stage');
+ for (const stage of stages) {
+ const mid = stage.attr().music_index
+ const type = stage.attr().sheet_type
+
+ const key = `${mid}:${type}`;
+ const c = stage.element('common');
+ const o = _.get(scoreData, `scores.${key}`, {});
+ const isHigh = c.number('score', 0) >= _.get(o, 'score', 0);
+ scoreData.scores[key] = {
+ score: Math.max(c.number('score', 0), _.get(o, 'score', 0)),
+ grade: Math.max(c.number('grade_basic', 0), _.get(o, 'grade', 0)),
+ recital: Math.max(c.number('grade_recital', 0), _.get(o, 'recital', 0)),
+ mode: isHigh ? c.number('hands_mode', 0) : _.get(o, 'mode', 0),
+ count: Math.max(c.number('play_count', 0), _.get(o, 'count', 1)),
+ clear: c.number('clear_count', _.get(o, 'clear', 0)),
+ multi: c.number('multi_count', _.get(o, 'multi', 0)),
+ flag: Math.max(c.number('clear_flag', 0), _.get(o, 'flag', 0)),
+ };
+ }
+
+ // Save Recitals
+ const rInfo = $(data).element('recital_info.recital');
+ if (rInfo){
+ const rIndex = rInfo.number('recital_index', -1);
+ if (rIndex >= 0) {
+ const r = rInfo.element('result');
+ const o = _.get(scoreData, `recitals.${rIndex}`);
+ const isHigh = r.number('total_score', 0) >= _.get(o, 'score', 0);
+ scoreData.recitals[rIndex] = {
+ count: Math.max(rInfo.number('recital_count', 0), _.get(o, 'count', 1)),
+ hall: isHigh ? rInfo.number('hall_index') : _.get(o, 'hall', 0),
+ cat: isHigh ? rInfo.numbers('cat_index') : _.get(o, 'cat', [3, 7, 9, 5, 16]),
+ audience: isHigh ? r.number('audience') : _.get(o, 'audience', 0),
+ money: isHigh ? r.number('money') : _.get(o, 'money', 0),
+ fame: isHigh ? r.number('fame') : _.get(o, 'fame', 0),
+ player: isHigh ? r.number('player_fame') : _.get(o, 'player', 0),
+ score: isHigh ? r.number('total_score', 0) : _.get(o, 'score', 0),
+ start: (isHigh ? r.number('recital_start_time', 0) : _.get(o, 'start', 0)).toString(),
+ end: (isHigh ? r.number('recital_end_time', 0) : _.get(o, 'end', 0)).toString(),
+ };
+ }
+ }
+
+ await writeScores(refid, scoreData);
+
+ send.success()
+};
+
+export const get_musicdata: EPR = async (info, data, send) => {
+ const refid = $(data).str('refid');
+ if (!refid) return send.deny();
+
+ const scoreData = await readScores(refid);
+
+ const recital_record: any[] = [];
+ const music: any[] = [];
+
+ for (const r in scoreData.recitals) {
+ const reci = scoreData.recitals[r];
+ recital_record.push({
+ recital_index: K.ITEM('s32', parseInt(r, 10)),
+ recital_count: K.ITEM('s32', reci.count),
+ hall_index: K.ITEM('s8', reci.hall),
+ cat_index: K.ARRAY('s16', reci.cat),
+ audience: K.ITEM('s32', reci.audience),
+ money: K.ITEM('s32', reci.money),
+ fame: K.ITEM('s32', reci.fame),
+ player_fame: K.ITEM('s32', reci.player),
+ total_score: K.ITEM('s32', reci.score),
+ total_base_score: K.ITEM('s32', reci.score),
+ best_start_time: K.ITEM('u64', BigInt(reci.start)),
+ best_end_time: K.ITEM('u64', BigInt(reci.end)),
+ });
+ }
+
+ for (const m in scoreData.scores) {
+ const mdata = m.split(':');
+ const musi = scoreData.scores[m];
+
+ music.push(K.ATTR({
+ music_index: mdata[0],
+ sheet_type: mdata[1],
+ }, {
+ 'score': K.ITEM('s32', musi.score),
+ 'grade_basic': K.ITEM('u32', musi.grade),
+ 'grade_recital': K.ITEM('u32', musi.recital),
+ 'play_count': K.ITEM('s32', musi.count),
+ 'clear_count': K.ITEM('s32', musi.clear),
+ 'multi_count': K.ITEM('s32', musi.multi),
+ 'hands_mode': K.ITEM('s8', musi.mode),
+ 'clear_flag': K.ITEM('s32', musi.flag),
+ }));
+ }
+
+ send.object({
+ recital_record,
+ music,
+ });
+};
+
+async function readProfile(refid: string): Promise {
+ const profile = await DB.FindOne(refid, { collection: 'profile'} )
+ return profile || defaultProfile
+}
+
+async function writeProfile(refid: string, profile: Profile) {
+ await DB.Upsert(refid, { collection: 'profile'}, profile)
+}
+
+async function readScores(refid: string): Promise {
+ const score = await DB.FindOne(refid, { collection: 'scores'} )
+ return score || { collection: 'scores', recitals: {}, scores: {}}
+}
+
+async function writeScores(refid: string, scores: Scores) {
+ await DB.Upsert(refid, { collection: 'scores'}, scores)
+}
+
+const defaultProfile: Profile = {
+ collection: 'profile',
+
+ name: 'GUEST',
+ music: 0,
+ sheet: 0,
+ brooch: 0,
+ hispeed: 0,
+ beatGuide: 1,
+ headphone: 0,
+ judgeBar: 250,
+ group: 0,
+ mode: 0,
+ near: 0,
+ offset: 0,
+ bingo: 0,
+ skill: '0',
+ playCount: 0,
+ todayPlayCount: 0,
+ keyBeam: 0,
+ orbit: 0,
+ noteHeight: 10,
+ noteWidth: 0,
+ judgeWidth: 0,
+ beatVolume: 0,
+ beatType: 0,
+ keyVolume: 0,
+ bgmVolume: 0,
+ note: 0,
+ sf: 0,
+ judgeFX: 0,
+ simple: 0,
+ money: 0,
+ fame: 0,
+ fameId: 0,
+ island: 0,
+ brooches: {
+ '1': {
+ level: 1,
+ watch: 0,
+ steps: 0,
+ new: 0,
+ },
+ },
+ islands: {},
+ kentei: {},
+ params: {
+ '1': [0],
+ },
+ musicList: {
+ type_0: Array(32).fill(-1),
+ type_1: Array(32).fill(-1),
+ type_2: Array(32).fill(-1),
+ type_3: Array(32).fill(-1),
+ },
+ musicList2: {
+ type_0: Array(32).fill(-1),
+ type_1: Array(32).fill(-1),
+ type_2: Array(32).fill(-1),
+ type_3: Array(32).fill(-1),
+ },
+}
diff --git a/nostalgia@asphyxia/index.ts b/nostalgia@asphyxia/index.ts
new file mode 100644
index 0000000..faad91c
--- /dev/null
+++ b/nostalgia@asphyxia/index.ts
@@ -0,0 +1,28 @@
+import { get_common_info, get_music_info } from "./handler/common";
+import { get_musicdata, get_playdata, regist_playdata, set_total_result} from "./handler/player"
+
+export function register() {
+ R.GameCode('PAN');
+
+ const MultiRoute = (method: string, handler: EPR | boolean) => {
+ // Helper for register multiple versions.
+ // But.. only Opus 2 for now.
+ R.Route(`op2_${method}`, handler);
+ };
+
+ const CommonRoute = (method: string, handler: EPR | boolean) =>
+ MultiRoute(`common.${method}`, handler)
+
+ const PlayerRoute = (method: string, handler: EPR | boolean) =>
+ MultiRoute(`player.${method}`, handler)
+
+ // Common
+ CommonRoute('get_common_info', get_common_info);
+ CommonRoute('get_music_info', get_music_info);
+
+ // Player
+ PlayerRoute('get_musicdata', get_musicdata)
+ PlayerRoute('get_playdata', get_playdata)
+ PlayerRoute('regist_playdata', regist_playdata)
+ PlayerRoute('set_total_result', set_total_result)
+}
\ No newline at end of file
diff --git a/nostalgia@asphyxia/models/profile.ts b/nostalgia@asphyxia/models/profile.ts
new file mode 100644
index 0000000..9b9fa02
--- /dev/null
+++ b/nostalgia@asphyxia/models/profile.ts
@@ -0,0 +1,82 @@
+export interface Profile {
+ collection: 'profile',
+
+ name: string;
+ playCount: number;
+ todayPlayCount: number;
+ music: number;
+ sheet: number;
+ brooch: number;
+ hispeed: number;
+ beatGuide: number;
+ headphone: number;
+ judgeBar: number;
+ group: number;
+ mode: number;
+ near: number;
+ offset: number;
+ bingo: number;
+ skill: string;
+ keyBeam: number;
+ orbit: number;
+ noteHeight: number;
+ noteWidth: number;
+ judgeWidth: number;
+ beatVolume: number;
+ beatType: number;
+ keyVolume: number;
+ bgmVolume: number;
+ note: number;
+ sf: number;
+ judgeFX: number;
+ simple: number;
+ money: number;
+ fame: number;
+ fameId: number;
+ island: number;
+ params: {
+ [key: string]: number[];
+ };
+ brooches: {
+ [key: string]: {
+ watch: number;
+ level: number;
+ steps: number;
+ new: number;
+ };
+ };
+ islands: {
+ [key: string]: {
+ look: number;
+ select: number;
+ time: number;
+ containers: {
+ [key: string]: {
+ prog: number;
+ rewards: { [key: string]: number };
+ };
+ };
+ };
+ };
+ kentei: {
+ [key: string]: {
+ stage: number;
+ score: number[];
+ rate: number;
+ flag: number;
+ count: number;
+ };
+ };
+ musicList: {
+ type_0: number[];
+ type_1: number[];
+ type_2: number[];
+ type_3: number[];
+ };
+ musicList2: {
+ type_0: number[];
+ type_1: number[];
+ type_2: number[];
+ type_3: number[];
+ };
+ }
\ No newline at end of file
diff --git a/nostalgia@asphyxia/models/scores.ts b/nostalgia@asphyxia/models/scores.ts
new file mode 100644
index 0000000..7a25669
--- /dev/null
+++ b/nostalgia@asphyxia/models/scores.ts
@@ -0,0 +1,30 @@
+export interface Scores {
+ collection: 'scores',
+
+ recitals: {
+ [key: string]: {
+ count: number;
+ hall: number;
+ cat: number[];
+ audience: number;
+ money: number;
+ fame: number;
+ player: number;
+ score: number;
+ start: string;
+ end: string;
+ };
+ };
+ scores: {
+ [key: string]: {
+ score: number;
+ grade: number;
+ recital: number;
+ count: number;
+ clear: number;
+ multi: number;
+ mode: number;
+ flag: number;
+ };
+ };
+ }
\ No newline at end of file