mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-22 23:18:05 +03:00
feat: iidx seeds and rerunners update (#1711)
* fix: update iidx-mdb-parse for v3 * fix: iidx-mdb-parse: check if legg conflict warning is necessary * seed: update iidx to 20260616 * feat: enable ferv imports for iidx 33 * seed: fix iidx infinitas->ac ports * seed: update IIDX SP12 tiers * feat: IIDX SP12 (and 11 sorta) tierlist update rerunner * seed: iidx eamuse csv titles update * refactor: don't expose randomHex, it's an internal fn, just inline again for now until I fix things up --------- Co-authored-by: zk <zkldi@proton.me>
This commit is contained in:
+4002
-22
File diff suppressed because it is too large
Load Diff
+4719
-79
File diff suppressed because it is too large
Load Diff
+709
-1
@@ -3309,6 +3309,7 @@
|
||||
"artist": "AKIRA YAMAOKA",
|
||||
"data": {
|
||||
"displayVersion": "7",
|
||||
"eamusementCsvTitle": "i feel...",
|
||||
"genre": "TRANCE"
|
||||
},
|
||||
"id": "S19d35e0afd14495fc26",
|
||||
@@ -9848,6 +9849,7 @@
|
||||
"artist": "TAKA respect for J.S.B.",
|
||||
"data": {
|
||||
"displayVersion": "15",
|
||||
"eamusementCsvTitle": "Ubertreffen",
|
||||
"genre": "PROGRESSIVE BAROQUE"
|
||||
},
|
||||
"id": "S19d35e0b1de03f23552",
|
||||
@@ -10388,6 +10390,7 @@
|
||||
"artist": "上野圭市(aka. Babyweapon,DJ SWAMI)feat.星野奏子",
|
||||
"data": {
|
||||
"displayVersion": "16",
|
||||
"eamusementCsvTitle": "Punch Love 仮面",
|
||||
"genre": "E-DANCE POP"
|
||||
},
|
||||
"id": "S19d35e0b20ea1acdfc0",
|
||||
@@ -13832,6 +13835,7 @@
|
||||
"artist": "劇団レコード",
|
||||
"data": {
|
||||
"displayVersion": "20",
|
||||
"eamusementCsvTitle": "YELLOW FROG from Steel Chronicle",
|
||||
"genre": "DESERT ALTERNATIVE EDGE"
|
||||
},
|
||||
"id": "S19d35e0b32c0098974d",
|
||||
@@ -13846,6 +13850,7 @@
|
||||
"artist": "dj TAKA feat.wac & secret K",
|
||||
"data": {
|
||||
"displayVersion": "20",
|
||||
"eamusementCsvTitle": "Time to Empress",
|
||||
"genre": "TRANCE"
|
||||
},
|
||||
"id": "S19d35e0b32dc934f33e",
|
||||
@@ -14625,6 +14630,7 @@
|
||||
"artist": "楓璃夢=ジークフリード=ファイエルローゼン",
|
||||
"data": {
|
||||
"displayVersion": "21",
|
||||
"eamusementCsvTitle": "旋律のドグマ~Miserables~",
|
||||
"genre": "JUVENILE DRUM & BASS"
|
||||
},
|
||||
"id": "S19d35e0b36cf10b8206",
|
||||
@@ -14756,6 +14762,7 @@
|
||||
"artist": "天叢雲剣",
|
||||
"data": {
|
||||
"displayVersion": "21",
|
||||
"eamusementCsvTitle": "Close the World feat.a☆ru",
|
||||
"genre": "J-CORE"
|
||||
},
|
||||
"id": "S19d35e0b376b3400853",
|
||||
@@ -15362,6 +15369,7 @@
|
||||
"artist": "TAG underground",
|
||||
"data": {
|
||||
"displayVersion": "21",
|
||||
"eamusementCsvTitle": "ATHER",
|
||||
"genre": "ASTRAL HARDCORE"
|
||||
},
|
||||
"id": "S19d35e0b3a535522359",
|
||||
@@ -15798,6 +15806,7 @@
|
||||
"artist": "機龍",
|
||||
"data": {
|
||||
"displayVersion": "22",
|
||||
"eamusementCsvTitle": "Rave*it!! Rave*it!! ",
|
||||
"genre": "EDM SPEED"
|
||||
},
|
||||
"id": "S19d35e0b3dcd6c8243d",
|
||||
@@ -16439,6 +16448,7 @@
|
||||
"artist": "Y&Co.",
|
||||
"data": {
|
||||
"displayVersion": "22",
|
||||
"eamusementCsvTitle": "TA・DA☆YO・SHI",
|
||||
"genre": "EUROBEAT"
|
||||
},
|
||||
"id": "S19d35e0b40e9bff9379",
|
||||
@@ -16913,6 +16923,7 @@
|
||||
"artist": "Hommarju",
|
||||
"data": {
|
||||
"displayVersion": "22",
|
||||
"eamusementCsvTitle": "Sounds Of Summer ",
|
||||
"genre": "UK HARDCORE"
|
||||
},
|
||||
"id": "S19d35e0b4341f5e547d",
|
||||
@@ -18270,6 +18281,7 @@
|
||||
"artist": "Ryu*",
|
||||
"data": {
|
||||
"displayVersion": "24",
|
||||
"eamusementCsvTitle": "!Viva!",
|
||||
"genre": "TRIBAL HOUSE"
|
||||
},
|
||||
"id": "S19d35e0b4c01dddda3d",
|
||||
@@ -18479,6 +18491,7 @@
|
||||
"artist": "RoughSkreamZ feat.楽天斎",
|
||||
"data": {
|
||||
"displayVersion": "24",
|
||||
"eamusementCsvTitle": "火影",
|
||||
"genre": "HIP HOP"
|
||||
},
|
||||
"id": "S19d35e0b4d9a7e63d39",
|
||||
@@ -19331,6 +19344,7 @@
|
||||
"artist": "BEMANI Sound Team \"TAG\"",
|
||||
"data": {
|
||||
"displayVersion": "26",
|
||||
"eamusementCsvTitle": "BLOSSOM",
|
||||
"genre": "BLOOM TRANCE"
|
||||
},
|
||||
"id": "S19d35e0b51d9e2dc98c",
|
||||
@@ -19488,6 +19502,7 @@
|
||||
"artist": "kors k",
|
||||
"data": {
|
||||
"displayVersion": "26",
|
||||
"eamusementCsvTitle": "Macho Monky",
|
||||
"genre": "JUNGLE TERROR"
|
||||
},
|
||||
"id": "S19d35e0b5295ee51691",
|
||||
@@ -19743,6 +19758,7 @@
|
||||
"artist": "Y&Co.",
|
||||
"data": {
|
||||
"displayVersion": "26",
|
||||
"eamusementCsvTitle": "ROCK女 feat. 大山愛未, Ken",
|
||||
"genre": "TRANCE"
|
||||
},
|
||||
"id": "S19d35e0b53d3ccd451d",
|
||||
@@ -19997,6 +20013,7 @@
|
||||
"artist": "sasakure.UK",
|
||||
"data": {
|
||||
"displayVersion": "26",
|
||||
"eamusementCsvTitle": "Xlo",
|
||||
"genre": "NEUTRONIKA"
|
||||
},
|
||||
"id": "S19d35e0b5516f90f625",
|
||||
@@ -20356,6 +20373,7 @@
|
||||
"artist": "Maozon",
|
||||
"data": {
|
||||
"displayVersion": "26",
|
||||
"eamusementCsvTitle": "Geirskogul",
|
||||
"genre": "CYBER DRUMSTEP"
|
||||
},
|
||||
"id": "S19d35e0b56d7e9d3f4e",
|
||||
@@ -20894,6 +20912,7 @@
|
||||
"artist": "RoughSketch",
|
||||
"data": {
|
||||
"displayVersion": "25",
|
||||
"eamusementCsvTitle": "Life Is A Game ft.DD\"ナカタ\"",
|
||||
"genre": "UPTEMPO HARDCORE"
|
||||
},
|
||||
"id": "S19d35e0b5991c24de0b",
|
||||
@@ -21285,6 +21304,7 @@
|
||||
"artist": "D-Evoke(与那嶺雅人/小日向翔)",
|
||||
"data": {
|
||||
"displayVersion": "25",
|
||||
"eamusementCsvTitle": "共犯ヘヴンズコード",
|
||||
"genre": "ELEGANT SWING"
|
||||
},
|
||||
"id": "S19d35e0b5b8bd078201",
|
||||
@@ -21785,6 +21805,7 @@
|
||||
"artist": "MOSAIC.WAV",
|
||||
"data": {
|
||||
"displayVersion": "25",
|
||||
"eamusementCsvTitle": "パラドキシカル・タイムリープトライアル(Short.Ver)",
|
||||
"genre": "AKIBA-POP"
|
||||
},
|
||||
"id": "S19d35e0b5e004a8b9df",
|
||||
@@ -21837,6 +21858,7 @@
|
||||
"artist": "Dirty Androids",
|
||||
"data": {
|
||||
"displayVersion": "25",
|
||||
"eamusementCsvTitle": "City Never Sleeps (IIDX EDITION)",
|
||||
"genre": "DRUM&BASS"
|
||||
},
|
||||
"id": "S19d35e0b5e4fcfbab69",
|
||||
@@ -21911,6 +21933,7 @@
|
||||
"artist": "BEMANI Sound Team \"Sota F.\"",
|
||||
"data": {
|
||||
"displayVersion": "25",
|
||||
"eamusementCsvTitle": "Let's bounce !!",
|
||||
"genre": "FUTURE HOUSE"
|
||||
},
|
||||
"id": "S19d35e0b5ea8fddea95",
|
||||
@@ -22017,6 +22040,7 @@
|
||||
"artist": "T.Σ.Generation",
|
||||
"data": {
|
||||
"displayVersion": "26",
|
||||
"eamusementCsvTitle": "Χ-DEN",
|
||||
"genre": "ULTIΣATE COLLABORATION"
|
||||
},
|
||||
"id": "S19d35e0b5f2303164f0",
|
||||
@@ -22107,6 +22131,7 @@
|
||||
"artist": "猫叉Master",
|
||||
"data": {
|
||||
"displayVersion": "20",
|
||||
"eamusementCsvTitle": "Reflection Into the EDEN",
|
||||
"genre": "BOSSA ELECTRONICA"
|
||||
},
|
||||
"id": "S19d35e0b5f9e932a3da",
|
||||
@@ -22210,6 +22235,7 @@
|
||||
"artist": "BEMANI Sound Team \"レ・度胸兄弟\"",
|
||||
"data": {
|
||||
"displayVersion": "27",
|
||||
"eamusementCsvTitle": "Dans la nuit de l'eternite",
|
||||
"genre": "POLONAISE"
|
||||
},
|
||||
"id": "S19d35e0b602eaa58303",
|
||||
@@ -23578,6 +23604,7 @@
|
||||
"artist": "BEMANI Sound Team Sota Fujimori",
|
||||
"data": {
|
||||
"displayVersion": "28",
|
||||
"eamusementCsvTitle": "Breakin' Rules",
|
||||
"genre": "CYBER PUNK"
|
||||
},
|
||||
"id": "S19d35e0b670854ca114",
|
||||
@@ -23592,6 +23619,7 @@
|
||||
"artist": "BEMANI Sound Team Sota Fujimori",
|
||||
"data": {
|
||||
"displayVersion": "28",
|
||||
"eamusementCsvTitle": "Hands Up feat. kradness BEMANI Sound Team \"Sota Fujimori\" Remix",
|
||||
"genre": "THE 4TH"
|
||||
},
|
||||
"id": "S19d35e0b6714d682759",
|
||||
@@ -23882,6 +23910,7 @@
|
||||
"artist": "Nhato",
|
||||
"data": {
|
||||
"displayVersion": "28",
|
||||
"eamusementCsvTitle": "Dont Stop The Music feat. Kanae Asaba",
|
||||
"genre": "ELECTRO FUNK"
|
||||
},
|
||||
"id": "S19d35e0b688cdf86f5a",
|
||||
@@ -23923,6 +23952,7 @@
|
||||
"artist": "かめりあ feat. ななひら",
|
||||
"data": {
|
||||
"displayVersion": "28",
|
||||
"eamusementCsvTitle": "POLKAMANIA",
|
||||
"genre": "ポルカ2020"
|
||||
},
|
||||
"id": "S19d35e0b68b00229000",
|
||||
@@ -24469,6 +24499,7 @@
|
||||
"artist": "BEMANI Sound Team \"U1 overground\"",
|
||||
"data": {
|
||||
"displayVersion": "28",
|
||||
"eamusementCsvTitle": "Parvati",
|
||||
"genre": "ORBITALIC WORLD MUSIC"
|
||||
},
|
||||
"id": "S19d35e0b6b7cea13e16",
|
||||
@@ -24715,6 +24746,7 @@
|
||||
"artist": "BEMANI Sound Team \"神\"",
|
||||
"data": {
|
||||
"displayVersion": "28",
|
||||
"eamusementCsvTitle": "Ignis†Irae",
|
||||
"genre": "X"
|
||||
},
|
||||
"id": "S19d35e0b6cbe5666c1e",
|
||||
@@ -25788,6 +25820,7 @@
|
||||
"artist": "MK",
|
||||
"data": {
|
||||
"displayVersion": "29",
|
||||
"eamusementCsvTitle": "Get set, Go! feat.Kanae Asaba",
|
||||
"genre": "FUTURE BOUNCE"
|
||||
},
|
||||
"id": "S19d35e0b7237cb6d652",
|
||||
@@ -26840,6 +26873,7 @@
|
||||
"artist": "かめりあ",
|
||||
"data": {
|
||||
"displayVersion": "30",
|
||||
"eamusementCsvTitle": "Ou Legends",
|
||||
"genre": "TOHOKU NEURO DENSETSU"
|
||||
},
|
||||
"id": "S19d35e0b778cda770ea",
|
||||
@@ -27472,6 +27506,7 @@
|
||||
"artist": "GeMiNioИ",
|
||||
"data": {
|
||||
"displayVersion": "30",
|
||||
"eamusementCsvTitle": "RINNE",
|
||||
"genre": "SYMPHONIC SAVIOR"
|
||||
},
|
||||
"id": "S19d35e0b7aa4f36d79f",
|
||||
@@ -27618,6 +27653,7 @@
|
||||
"artist": "天土",
|
||||
"data": {
|
||||
"displayVersion": "30",
|
||||
"eamusementCsvTitle": "異心前進♪ぴょんぴょんぴょん ",
|
||||
"genre": "EUROBEAT"
|
||||
},
|
||||
"id": "S19d35e0b7b5c5fe360d",
|
||||
@@ -28962,6 +28998,7 @@
|
||||
"artist": "モリモリあつし",
|
||||
"data": {
|
||||
"displayVersion": "31",
|
||||
"eamusementCsvTitle": "ACT0",
|
||||
"genre": "NEOTRANCE CORE"
|
||||
},
|
||||
"id": "S19d35e0b81dbf132cab",
|
||||
@@ -29487,6 +29524,7 @@
|
||||
"artist": "BEMANI Sound Team \"TATSUYA\" feat. ななひら",
|
||||
"data": {
|
||||
"displayVersion": "32",
|
||||
"eamusementCsvTitle": "最強頂戴girl",
|
||||
"genre": "TWINKLE POP"
|
||||
},
|
||||
"id": "S19d35e0b84637d5b3be",
|
||||
@@ -29637,6 +29675,7 @@
|
||||
"artist": "BlackY feat. Risa Yuzuki",
|
||||
"data": {
|
||||
"displayVersion": "32",
|
||||
"eamusementCsvTitle": "FERMI PARADOX",
|
||||
"genre": "COSMIC HAPPY TUNE"
|
||||
},
|
||||
"id": "S19d35e0b8522759e6f6",
|
||||
@@ -29727,6 +29766,7 @@
|
||||
"artist": "lapix feat. PANXI",
|
||||
"data": {
|
||||
"displayVersion": "32",
|
||||
"eamusementCsvTitle": "オッド ラヴィ",
|
||||
"genre": "LOVELY TECHCORE"
|
||||
},
|
||||
"id": "S19d35e0b85914e9c8a3",
|
||||
@@ -29883,6 +29923,7 @@
|
||||
"artist": "Tatsunoshin & 藍良あれれ",
|
||||
"data": {
|
||||
"displayVersion": "32",
|
||||
"eamusementCsvTitle": "Raspberry Potion (feat.あれたん & ぎゃるのしん☆)",
|
||||
"genre": "LOVE♡CORE"
|
||||
},
|
||||
"id": "S19d35e0b8658b46a23f",
|
||||
@@ -31065,10 +31106,13 @@
|
||||
"title": "From Human Blood"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"altTitles": [
|
||||
"Space Battleship S4TO"
|
||||
],
|
||||
"artist": "BEMANI Sound Team \"Captain Sonic\"",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"eamusementCsvTitle": "Space Battleship S4TO",
|
||||
"genre": "BIGCORE"
|
||||
},
|
||||
"id": "S19d35e0b8c03f018a8e",
|
||||
@@ -31789,5 +31833,669 @@
|
||||
"Rush"
|
||||
],
|
||||
"title": "Rush!!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "ビートまりお Prod. by Yuta Imai & L.E.D.-G",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HARD MIXTURE"
|
||||
},
|
||||
"id": "S19f39a108de1fe0d14d",
|
||||
"legacySongID": 2607,
|
||||
"searchTerms": [],
|
||||
"title": "HEROES"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "cosMo@暴走P & L.E.D.-G",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "Hi-ROM COM"
|
||||
},
|
||||
"id": "S19f39a108de64e81754",
|
||||
"legacySongID": 2606,
|
||||
"searchTerms": [
|
||||
"rom com sometime serious"
|
||||
],
|
||||
"title": "ラブコメトキドキシリアス"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "red glasses vs Ujico*",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "FM JAZZY TECH"
|
||||
},
|
||||
"id": "S19f39a108de8cd7ed96",
|
||||
"legacySongID": 2609,
|
||||
"searchTerms": [],
|
||||
"title": "BitRacer"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "PHQUASE vs. dj TAKA",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "DOUBLE DRIVE"
|
||||
},
|
||||
"id": "S19f39a108de914e528b",
|
||||
"legacySongID": 2612,
|
||||
"searchTerms": [],
|
||||
"title": "fixer"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "BEMANI Sound Team \"DJ TOTTO\"",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "FUTURISTIC 12 BEAT"
|
||||
},
|
||||
"id": "S19f39a108dea0e3e8c6",
|
||||
"legacySongID": 2610,
|
||||
"searchTerms": [],
|
||||
"title": "chaplet -IIDX re:build-"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "ネオン(CV:藤川茜)、芽兎めう(CV:五十嵐裕美)",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "PROJECT ZIRCON"
|
||||
},
|
||||
"id": "S19f39a108deb48d1cff",
|
||||
"legacySongID": 2611,
|
||||
"searchTerms": [],
|
||||
"title": "Signs and Wonders"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "TAN1CHU",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "EMOTIONAL FUTURE RIDDIM"
|
||||
},
|
||||
"id": "S19f39a108def65b88a2",
|
||||
"legacySongID": 2608,
|
||||
"searchTerms": [],
|
||||
"title": "Nemophila"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Nhato",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "CELTIC CORE"
|
||||
},
|
||||
"id": "S19f39a108df108312d9",
|
||||
"legacySongID": 2622,
|
||||
"searchTerms": [],
|
||||
"title": "Dusk Endir"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "TANUKI",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "YALDICORE"
|
||||
},
|
||||
"id": "S19f39a108df15f44c71",
|
||||
"legacySongID": 2640,
|
||||
"searchTerms": [],
|
||||
"title": "HyperTwist"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "葛城ユイ feat. Command (Eurobeat Union)",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "EUROBEAT"
|
||||
},
|
||||
"id": "S19f39a108df188d1e32",
|
||||
"legacySongID": 2621,
|
||||
"searchTerms": [],
|
||||
"title": "UNDO THE NIGHT"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "DJ YOSHITAKA",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HARD RENAISSANCE"
|
||||
},
|
||||
"id": "S19f39a108df275b58cd",
|
||||
"legacySongID": 2628,
|
||||
"searchTerms": [],
|
||||
"title": "Lisa-RICCIA"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Plantica",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "POP"
|
||||
},
|
||||
"id": "S19f39a108df2deff094",
|
||||
"legacySongID": 2614,
|
||||
"searchTerms": [],
|
||||
"title": "Bloom"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Ice vs BEMANI Sound Team \"dj TAKA\"",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "ROMANTIC SCHOOL VARIATIONS"
|
||||
},
|
||||
"id": "S19f39a108df2f57065f",
|
||||
"legacySongID": 2616,
|
||||
"searchTerms": [
|
||||
"Flying beyond reason"
|
||||
],
|
||||
"title": "⁽⁽ଘ( ˙꒳˙ )ଓ⁾⁾ beyond reason"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "OSTER project",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "BOLLYWOOD CORE"
|
||||
},
|
||||
"id": "S19f39a108df39333a05",
|
||||
"legacySongID": 2625,
|
||||
"searchTerms": [
|
||||
"Bengal vs. Amur Battle"
|
||||
],
|
||||
"title": "ベンガル vs. アムール Battle"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Zekk",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "SUPER ULTRA MIRACLE HAPPY HARDCORE"
|
||||
},
|
||||
"id": "S19f39a108df434e13be",
|
||||
"legacySongID": 2624,
|
||||
"searchTerms": [],
|
||||
"title": "PERFECT GREAT!!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "crayvxn",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HYPERTRANCE"
|
||||
},
|
||||
"id": "S19f39a108df448fde00",
|
||||
"legacySongID": 2618,
|
||||
"searchTerms": [],
|
||||
"title": "voice of echo."
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "kors k",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "CHANNEL K"
|
||||
},
|
||||
"id": "S19f39a108df4820c03e",
|
||||
"legacySongID": 2617,
|
||||
"searchTerms": [],
|
||||
"title": "kors k's Let's make an Image Song!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "かめりあ feat. かめりあ",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "GREY DISTRICT"
|
||||
},
|
||||
"id": "S19f39a108df53500318",
|
||||
"legacySongID": 2639,
|
||||
"searchTerms": [
|
||||
"irowoushinattamachi"
|
||||
],
|
||||
"title": "色を喪った街"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "めめめ",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "超炭酸すぱしゃわコア"
|
||||
},
|
||||
"id": "S19f39a108df5c783cfa",
|
||||
"legacySongID": 2615,
|
||||
"searchTerms": [
|
||||
"fizzy potion"
|
||||
],
|
||||
"title": "FiZZλ_PØT!0И"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Whac-A-Me",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "PSY-TRANCE"
|
||||
},
|
||||
"id": "S19f39a108df6043f179",
|
||||
"legacySongID": 2633,
|
||||
"searchTerms": [],
|
||||
"title": "Begin"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Dirty Androids",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "UPTEMPO ELECTRO FUNK"
|
||||
},
|
||||
"id": "S19f39a108df60cf3e1b",
|
||||
"legacySongID": 2623,
|
||||
"searchTerms": [],
|
||||
"title": "Funky Quirky"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Remixed by Pizuya's Cell",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "IIDX EDITION"
|
||||
},
|
||||
"id": "S19f39a108df6128f60d",
|
||||
"legacySongID": 2635,
|
||||
"searchTerms": [
|
||||
"Override"
|
||||
],
|
||||
"title": "オーバーライド"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "中山真斗 feat. CHAN",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "ALTERNATIVE"
|
||||
},
|
||||
"id": "S19f39a108df67291279",
|
||||
"legacySongID": 2627,
|
||||
"searchTerms": [
|
||||
"Haochi Raise de Electric"
|
||||
],
|
||||
"title": "好吃来世deエレクトリック"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "MYUKKE.",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "ORIENTAL MIRAGE"
|
||||
},
|
||||
"id": "S19f39a108df69191b6d",
|
||||
"legacySongID": 2638,
|
||||
"searchTerms": [],
|
||||
"title": "TOKAKU=ALMiRAJ"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "YUC'e",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "EXPLO POP"
|
||||
},
|
||||
"id": "S19f39a108df87e08b46",
|
||||
"legacySongID": 2636,
|
||||
"searchTerms": [],
|
||||
"title": "Watermelon Explosion"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "kors k",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "BEBOP CORE"
|
||||
},
|
||||
"id": "S19f39a108df8feae8ba",
|
||||
"legacySongID": 2641,
|
||||
"searchTerms": [],
|
||||
"title": "Last Card"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Qlarabelle",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "SYMPHONIC HARD TECHNO"
|
||||
},
|
||||
"id": "S19f39a108dfa28cd3f6",
|
||||
"legacySongID": 2631,
|
||||
"searchTerms": [],
|
||||
"title": "OMEGA"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Tom-H@ck feat.Nadia",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "DIGITAL ROCK"
|
||||
},
|
||||
"id": "S19f39a108dfa8a0d32d",
|
||||
"legacySongID": 2629,
|
||||
"searchTerms": [],
|
||||
"title": "X-Plan"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Blacklolita",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "DUBSTEP"
|
||||
},
|
||||
"id": "S19f39a108dfaed47950",
|
||||
"legacySongID": 2626,
|
||||
"searchTerms": [],
|
||||
"title": "BattleRoyal"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Remixed by BEMANI Sound Team \"めめめ\" feat. beatnation Records",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "RECKLESS RAVE"
|
||||
},
|
||||
"id": "S19f39a108dfb4a5a838",
|
||||
"legacySongID": 2632,
|
||||
"searchTerms": [],
|
||||
"title": "GOLD RUSH 2025"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Virtuoso",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "CLASSICAL"
|
||||
},
|
||||
"id": "S19f39a108dfc549bd05",
|
||||
"legacySongID": 2613,
|
||||
"searchTerms": [
|
||||
"Crescendo"
|
||||
],
|
||||
"title": "クレッシェンド"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "ピノキオピー",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "ボカロ"
|
||||
},
|
||||
"id": "S19f39a108dfc7d3d4f1",
|
||||
"legacySongID": 2634,
|
||||
"searchTerms": [
|
||||
"Playing Immortal"
|
||||
],
|
||||
"title": "不死身ごっこ"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "KO3 & Relect",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "PSYCHEDELIC TRANCE"
|
||||
},
|
||||
"id": "S19f39a108dfd1d5370e",
|
||||
"legacySongID": 2620,
|
||||
"searchTerms": [],
|
||||
"title": "Hallucination"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "猫叉Master feat.常盤ゆう",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "フルフィーポップ"
|
||||
},
|
||||
"id": "S19f39a108dfd2a0b711",
|
||||
"legacySongID": 2637,
|
||||
"searchTerms": [],
|
||||
"title": "REcorrection"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "MK&Kanae Asaba",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HYBRID JERSY CLUB"
|
||||
},
|
||||
"id": "S19f39a108dfd73ece7b",
|
||||
"legacySongID": 2619,
|
||||
"searchTerms": [],
|
||||
"title": "Show Time"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "BlackY",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "PIANO DANCE APPASSIONATO"
|
||||
},
|
||||
"id": "S19f39a108dff00b9fac",
|
||||
"legacySongID": 2630,
|
||||
"searchTerms": [
|
||||
"Amor Fati"
|
||||
],
|
||||
"title": "Amor∞Fati"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "音戯探偵ひなビタ♫",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "ガールズティーパーティー"
|
||||
},
|
||||
"id": "S19f39a108e0053a92f8",
|
||||
"legacySongID": 2644,
|
||||
"searchTerms": [
|
||||
"Recipe Riddle"
|
||||
],
|
||||
"title": "レシピのリドル"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "TAN1CHU",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HAM PARADOX"
|
||||
},
|
||||
"id": "S19f39a108e008a706f0",
|
||||
"legacySongID": 2652,
|
||||
"searchTerms": [],
|
||||
"title": "Smintheus"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Ryu☆ Remixed by Mamonis",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HYPER BUCHIAGE TRANCE"
|
||||
},
|
||||
"id": "S19f39a108e00ba6e381",
|
||||
"legacySongID": 2648,
|
||||
"searchTerms": [],
|
||||
"title": "Second Heaven(Mamonis Remix)"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "音戯探偵ひなビタ♫",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "オトギタンテイ"
|
||||
},
|
||||
"id": "S19f39a108e0364c8ab5",
|
||||
"legacySongID": 2643,
|
||||
"searchTerms": [
|
||||
"The Great Otogitantei-Hinabita"
|
||||
],
|
||||
"title": "華麗なる!音戯探偵ひなビタ♫"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Dustup VS MAX MAXIMIZER",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HARD PSY/SPEEDCORE"
|
||||
},
|
||||
"id": "S19f39a108e0399f8734",
|
||||
"legacySongID": 2642,
|
||||
"searchTerms": [],
|
||||
"title": "RUINA"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "kors k Remixed by 青龍",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HYPER DANCE SPEED"
|
||||
},
|
||||
"id": "S19f39a108e039b37ee4",
|
||||
"legacySongID": 2647,
|
||||
"searchTerms": [
|
||||
"Gimme a Big Beat Seiryu Remix"
|
||||
],
|
||||
"title": "Gimme a Big Beat(青龍 Remix)"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "秀よし君",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "太閤ヒップホップ"
|
||||
},
|
||||
"id": "S19f39a108e04270852e",
|
||||
"legacySongID": 2657,
|
||||
"searchTerms": [
|
||||
"mikkadenkamonkey"
|
||||
],
|
||||
"title": "三日天下モンキー"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "星野音楽工房",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "MELODIOUS TECH"
|
||||
},
|
||||
"id": "S19f39a108e047009a54",
|
||||
"legacySongID": 2646,
|
||||
"searchTerms": [],
|
||||
"title": "pique"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "The 4th feat.TORIENA",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HYPER CANDY RAVE"
|
||||
},
|
||||
"id": "S19f39a108e05733bbe9",
|
||||
"legacySongID": 2649,
|
||||
"searchTerms": [],
|
||||
"title": "Kick The Rainbow"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "NU-KO",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "HAPPY LOVEGAZER"
|
||||
},
|
||||
"id": "S19f39a108e07e2c5c2c",
|
||||
"legacySongID": 2650,
|
||||
"searchTerms": [
|
||||
"RENAI KANSOKU"
|
||||
],
|
||||
"title": "恋愛観測"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "dj MAX STEROID",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "NU SKOOL BREAKS"
|
||||
},
|
||||
"id": "S19f39a108e099aa9c0b",
|
||||
"legacySongID": 2656,
|
||||
"searchTerms": [
|
||||
"kakkoii"
|
||||
],
|
||||
"title": "[E]"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Nhato",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "TRANCECORE"
|
||||
},
|
||||
"id": "S19f39a108e0a308aecc",
|
||||
"legacySongID": 2645,
|
||||
"searchTerms": [],
|
||||
"title": "Skywalking"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "BEMANI Sound Team \"akYm\" feat.maimie",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "POP ROCK"
|
||||
},
|
||||
"id": "S19f39a108e0ae3e2682",
|
||||
"legacySongID": 2655,
|
||||
"searchTerms": [
|
||||
"kimitoegakukiseki"
|
||||
],
|
||||
"title": "キミとえがくキセキ"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "BEMANI Sound Team \"HuΣeR\" feat.ゆきまめ",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "幻想音樂"
|
||||
},
|
||||
"id": "S19f39a108e0b9009b03",
|
||||
"legacySongID": 2651,
|
||||
"searchTerms": [
|
||||
"Telos"
|
||||
],
|
||||
"title": "Τέλος"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "KE!JU",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "JUDGEMENT SYMPHONIC SPEEDCORE"
|
||||
},
|
||||
"id": "S19f39a108e0c78848df",
|
||||
"legacySongID": 2653,
|
||||
"searchTerms": [
|
||||
"judgement mimesis"
|
||||
],
|
||||
"title": "断罪のミメシス"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "めめめ",
|
||||
"data": {
|
||||
"displayVersion": "33",
|
||||
"genre": "DTM SPEEDRUN"
|
||||
},
|
||||
"id": "S19f39a108e0e5096529",
|
||||
"legacySongID": 2654,
|
||||
"searchTerms": [
|
||||
"Any Percent"
|
||||
],
|
||||
"title": "Any%"
|
||||
}
|
||||
]
|
||||
@@ -214,7 +214,11 @@ export async function ParseIIDXData(
|
||||
const notecount = chart.notecount;
|
||||
|
||||
if (chart.difficulty === "LEGGENDARIA") {
|
||||
if (notecount !== 0 && notecounts[`${chart.playtype}-LEGGENDARIA`]) {
|
||||
if (
|
||||
notecount !== 0 &&
|
||||
notecounts[`${chart.playtype}-LEGGENDARIA`] &&
|
||||
notecount !== notecounts[`${chart.playtype}-LEGGENDARIA`]
|
||||
) {
|
||||
log.warn(
|
||||
`${chart.artist} - ${chart.title} has conflicting ${
|
||||
chart.playtype
|
||||
|
||||
@@ -3,23 +3,30 @@ import { Command } from "commander";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import {
|
||||
type ChartDocument,
|
||||
type SEEDS_ChartDocument,
|
||||
type Difficulties,
|
||||
type GamesForGroup,
|
||||
type integer,
|
||||
LEGACY_GetGamePTConfig,
|
||||
type SongDocument,
|
||||
GetGameConfig,
|
||||
type SEEDS_SongDocument,
|
||||
type Versions,
|
||||
} from "tachi-common";
|
||||
|
||||
import {
|
||||
CreateChartID,
|
||||
CreateSongID,
|
||||
GetFreshSongIDGenerator,
|
||||
ReadCollection,
|
||||
WriteCollection,
|
||||
} from "../../../util";
|
||||
import { ParseIIDXData } from "./convert";
|
||||
|
||||
function randomHex(bytes: any) {
|
||||
const buf = new Uint8Array(bytes);
|
||||
globalThis.crypto.getRandomValues(buf);
|
||||
return Array.from(buf, (b) => b.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
if (require.main !== module) {
|
||||
throw new Error(`This is a script. It should be ran directly from the command line with bun.`);
|
||||
}
|
||||
@@ -46,7 +53,7 @@ const options = program.opts() as {
|
||||
version: Versions["iidx-sp"];
|
||||
};
|
||||
|
||||
const iidxConfig = LEGACY_GetGamePTConfig("iidx", "SP");
|
||||
const iidxConfig = GetGameConfig("iidx-sp");
|
||||
|
||||
if (!Object.keys(iidxConfig.versions).includes(options.version)) {
|
||||
throw new Error(
|
||||
@@ -62,9 +69,10 @@ if (options.index !== "0" && options.index !== "1") {
|
||||
throw new Error(`Expected an --index of 0 or 1. Got ${options.index}.`);
|
||||
}
|
||||
|
||||
const existingCharts: ChartDocument<GamesForGroup["iidx"]>[] = ReadCollection("charts-iidx.json");
|
||||
const existingChartsSP: SEEDS_ChartDocument<"iidx-sp">[] = ReadCollection("charts-iidx-sp.json");
|
||||
const existingChartsDP: SEEDS_ChartDocument<"iidx-dp">[] = ReadCollection("charts-iidx-dp.json");
|
||||
|
||||
const existingSongs: SongDocument<"iidx">[] = ReadCollection("songs-iidx.json");
|
||||
const existingSongs: SEEDS_SongDocument<"iidx">[] = ReadCollection("songs-iidx.json");
|
||||
|
||||
const blacklist = fs
|
||||
.readFileSync(path.join(__dirname, "blacklist.txt"), "utf-8")
|
||||
@@ -99,17 +107,17 @@ async function ParseIIDXMDB() {
|
||||
options.alwaysExtract,
|
||||
);
|
||||
|
||||
const chartMap = new Map<integer, ChartDocument<GamesForGroup["iidx"]>>();
|
||||
const songMap = new Map<integer, SongDocument<"iidx">>();
|
||||
const songTitleMap = new Map<string, SongDocument<"iidx">>();
|
||||
const chartDiffMap = new Map<string, ChartDocument<GamesForGroup["iidx"]>>();
|
||||
const chartMap = new Map<integer, SEEDS_ChartDocument<GamesForGroup["iidx"]>>();
|
||||
const songMap = new Map<string, SEEDS_SongDocument<"iidx">>();
|
||||
const songTitleMap = new Map<string, SEEDS_SongDocument<"iidx">>();
|
||||
const chartDiffMap = new Map<string, SEEDS_ChartDocument<GamesForGroup["iidx"]>>();
|
||||
|
||||
for (const song of existingSongs) {
|
||||
songMap.set(song.id, song);
|
||||
songTitleMap.set(song.title, song);
|
||||
}
|
||||
|
||||
for (const chart of existingCharts) {
|
||||
for (const chart of [...existingChartsSP, ...existingChartsDP]) {
|
||||
// what, you thought this was easy?
|
||||
if (Array.isArray(chart.data.inGameID)) {
|
||||
for (const igid of chart.data.inGameID) {
|
||||
@@ -120,17 +128,27 @@ async function ParseIIDXMDB() {
|
||||
}
|
||||
}
|
||||
|
||||
for (const chart of existingCharts) {
|
||||
for (const chart of existingChartsSP) {
|
||||
if (Array.isArray(chart.data.inGameID)) {
|
||||
for (const igid of chart.data.inGameID) {
|
||||
chartDiffMap.set(`${igid}-${chart.playtype}-${chart.difficulty}`, chart);
|
||||
chartDiffMap.set(`${igid}-SP-${chart.difficulty}`, chart);
|
||||
}
|
||||
} else {
|
||||
chartDiffMap.set(`${chart.data.inGameID}-${chart.playtype}-${chart.difficulty}`, chart);
|
||||
chartDiffMap.set(`${chart.data.inGameID}-SP-${chart.difficulty}`, chart);
|
||||
}
|
||||
}
|
||||
|
||||
const getFreeSongID = GetFreshSongIDGenerator("iidx");
|
||||
for (const chart of existingChartsDP) {
|
||||
if (Array.isArray(chart.data.inGameID)) {
|
||||
for (const igid of chart.data.inGameID) {
|
||||
chartDiffMap.set(`${igid}-DP-${chart.difficulty}`, chart);
|
||||
}
|
||||
} else {
|
||||
chartDiffMap.set(`${chart.data.inGameID}-DP-${chart.difficulty}`, chart);
|
||||
}
|
||||
}
|
||||
|
||||
const getFreeLegacySongID = GetFreshSongIDGenerator("iidx");
|
||||
|
||||
for (const inp of mdbCharts) {
|
||||
if (isInBlacklist(`S${inp.songID}`)) {
|
||||
@@ -141,7 +159,7 @@ async function ParseIIDXMDB() {
|
||||
}
|
||||
|
||||
const anySongIDMatch = chartMap.get(inp.songID);
|
||||
let song: SongDocument<"iidx">;
|
||||
let song: SEEDS_SongDocument<"iidx">;
|
||||
|
||||
if (!anySongIDMatch) {
|
||||
// new song?
|
||||
@@ -168,8 +186,9 @@ async function ParseIIDXMDB() {
|
||||
searchTerms.push(inp.marquee);
|
||||
}
|
||||
|
||||
const tachiSong: SongDocument<"iidx"> = {
|
||||
id: getFreeSongID(),
|
||||
const tachiSong: SEEDS_SongDocument<"iidx"> = {
|
||||
id: CreateSongID(),
|
||||
legacySongID: getFreeLegacySongID(),
|
||||
artist: inp.artist,
|
||||
title: inp.title,
|
||||
data: {
|
||||
@@ -241,15 +260,13 @@ async function ParseIIDXMDB() {
|
||||
|
||||
// otherwise, make new chart?
|
||||
const playtypeStr = diffName.split("-")[0] as "DP" | "SP";
|
||||
const game: GamesForGroup["iidx"] = playtypeStr === "SP" ? "iidx-sp" : "iidx-dp";
|
||||
const tachiChart: ChartDocument<GamesForGroup["iidx"]> = {
|
||||
game,
|
||||
chartID: CreateChartID(),
|
||||
const tachiChart: SEEDS_ChartDocument<GamesForGroup["iidx"]> = {
|
||||
id: CreateChartID(),
|
||||
legacyChartID: randomHex(20),
|
||||
difficulty: diffName.split("-")[1] as Difficulties[GamesForGroup["iidx"]],
|
||||
level: level.toString(),
|
||||
levelNum: level,
|
||||
isPrimary: true,
|
||||
playtype: playtypeStr,
|
||||
|
||||
songID: song.id,
|
||||
versions: [options.version],
|
||||
@@ -265,7 +282,11 @@ async function ParseIIDXMDB() {
|
||||
};
|
||||
|
||||
log.info(`Inserting new chart ${inp.title} ${diffName}.`);
|
||||
existingCharts.push(tachiChart);
|
||||
if (playtypeStr === "SP") {
|
||||
existingChartsSP.push(tachiChart);
|
||||
} else {
|
||||
existingChartsDP.push(tachiChart);
|
||||
}
|
||||
} else {
|
||||
if (!notecount) {
|
||||
log.warn(
|
||||
@@ -304,7 +325,8 @@ async function ParseIIDXMDB() {
|
||||
}
|
||||
|
||||
WriteCollection("songs-iidx.json", existingSongs);
|
||||
WriteCollection("charts-iidx.json", existingCharts);
|
||||
WriteCollection("charts-iidx-sp.json", existingChartsSP);
|
||||
WriteCollection("charts-iidx-dp.json", existingChartsDP);
|
||||
}
|
||||
|
||||
ParseIIDXMDB();
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
import { log } from "#log";
|
||||
import * as cheerio from "cheerio";
|
||||
import { type AnyNode } from "domhandler";
|
||||
import { type SEEDS_ChartDocument, type SEEDS_SongDocument } from "tachi-common";
|
||||
import { ReadCollection, WriteCollection } from "../../util";
|
||||
|
||||
if (require.main !== module) {
|
||||
throw new Error(`This is a script. It should be ran directly from the command line with bun.`);
|
||||
}
|
||||
|
||||
const URL_L12_BASE =
|
||||
"https://docs.google.com/spreadsheets/u/0/d/e/2PACX-1vSUdp6iuEzE8Z5AL1hkoxzLexp89nJnLQMmICm6_MC0_UjCp1ImZFzabcZkvCpK7mcWvm_2t6iYoJRg/pubhtml/sheet?headers=false&gid=";
|
||||
const URL_L11_BASE =
|
||||
"https://docs.google.com/spreadsheets/u/0/d/e/2PACX-1vSLnjRLdYQ53QgdmKoCvW9zVSgHjBCcGf8iTeEgjiSia4i_4YwR9isSfhxC2kSAq_wWygd_9xGORhk0/pubhtml/sheet?headers=false&gid=";
|
||||
|
||||
const TIER_CONFIGS = [
|
||||
{
|
||||
url: URL_L12_BASE + "1184656976",
|
||||
level: 12,
|
||||
gauge: "normal",
|
||||
format: "columnwise",
|
||||
},
|
||||
{
|
||||
url: URL_L12_BASE + "1277599511",
|
||||
level: 12,
|
||||
gauge: "hard",
|
||||
format: "columnwise",
|
||||
},
|
||||
// The 11 sheet is out of date - trying to find a better one.
|
||||
// {
|
||||
// url: URL_L11_BASE + "411493762",
|
||||
// level: 11,
|
||||
// gauge: "normal",
|
||||
// format: "rowwise",
|
||||
// },
|
||||
// {
|
||||
// url: URL_L11_BASE + "1585306050",
|
||||
// level: 11,
|
||||
// gauge: "hard",
|
||||
// format: "rowwise",
|
||||
// },
|
||||
];
|
||||
|
||||
type songInTier = {
|
||||
title: string;
|
||||
difficulty: string;
|
||||
kojinsa: boolean;
|
||||
};
|
||||
|
||||
const KOJINSA_CLASS = "s5";
|
||||
|
||||
function editDistance(a: string, b: string) {
|
||||
const matrix = [];
|
||||
for (let i = 0; i <= b.length; i++) {
|
||||
matrix[i] = [i];
|
||||
}
|
||||
for (let j = 0; j <= a.length; j++) {
|
||||
matrix[0][j] = j;
|
||||
}
|
||||
for (let i = 1; i <= b.length; i++) {
|
||||
for (let j = 1; j <= a.length; j++) {
|
||||
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
||||
matrix[i][j] = matrix[i - 1][j - 1];
|
||||
} else {
|
||||
matrix[i][j] = Math.min(
|
||||
matrix[i - 1][j - 1] + 1,
|
||||
matrix[i][j - 1] + 1,
|
||||
matrix[i - 1][j] + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix[b.length][a.length];
|
||||
}
|
||||
|
||||
function parseTitleAndDiff(text: string) {
|
||||
// L12 sheet uses [L]/[H]; L11 sheet uses (L)/(H).
|
||||
if (text.endsWith("[L]") || text.endsWith("(L)")) {
|
||||
return { title: text.slice(0, -3).trim(), difficulty: "LEGGENDARIA" };
|
||||
}
|
||||
if (text.endsWith("[H]") || text.endsWith("(H)")) {
|
||||
return { title: text.slice(0, -3).trim(), difficulty: "HYPER" };
|
||||
}
|
||||
return { title: text.trim(), difficulty: "ANOTHER" };
|
||||
}
|
||||
|
||||
// NFKC folds fullwidth ASCII/digits to halfwidth (1 -> 1, + -> +); the regex
|
||||
// passes then collapse curly quotes which NFKC leaves as stylistic variants.
|
||||
function normalize(s: string) {
|
||||
return s.normalize("NFKC").replace(/[“”]/g, '"').replace(/[‘’]/g, "'");
|
||||
}
|
||||
|
||||
function findMatchingSong(title: string, songsData: SEEDS_SongDocument<"iidx">[]) {
|
||||
const waveDashed = title.replaceAll("~", "〜");
|
||||
const normalizedClean = normalize(waveDashed);
|
||||
|
||||
for (const song of songsData) {
|
||||
if (normalize(song.title) === normalizedClean) {
|
||||
return song;
|
||||
}
|
||||
}
|
||||
|
||||
const lowerTitle = normalizedClean.toLowerCase();
|
||||
let bestMatch = null;
|
||||
let bestDistance = Infinity;
|
||||
const maxDistance = 4;
|
||||
|
||||
for (const song of songsData) {
|
||||
const distance = editDistance(lowerTitle, normalize(song.title).toLowerCase().trim());
|
||||
if (distance <= maxDistance && distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestMatch = song;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch === null) {
|
||||
return null;
|
||||
}
|
||||
if (bestDistance > 0) {
|
||||
log.info(`Fuzzy match: "${title}" -> "${bestMatch.title}" (distance: ${bestDistance})`);
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
function isRedSpan(node: AnyNode) {
|
||||
if (node.type !== "tag" || node.name !== "span") {
|
||||
return false;
|
||||
}
|
||||
const style = node.attribs?.style || "";
|
||||
return /color\s*:\s*#ff0000/i.test(style);
|
||||
}
|
||||
|
||||
// Walks a <td>, returning [{title, difficulty, kojinsa}] split on <br>.
|
||||
// Any text encountered inside a red <span> marks its segment kojinsa=true.
|
||||
function parseCell(cellEl: AnyNode) {
|
||||
const titles: songInTier[] = [];
|
||||
let buffer = "";
|
||||
let bufferIsRed = false;
|
||||
|
||||
function flush() {
|
||||
const t = buffer.trim();
|
||||
if (t.length > 0) {
|
||||
const { title, difficulty } = parseTitleAndDiff(t);
|
||||
titles.push({ title: title, difficulty: difficulty, kojinsa: bufferIsRed });
|
||||
}
|
||||
buffer = "";
|
||||
bufferIsRed = false;
|
||||
}
|
||||
|
||||
function walk(nodes: AnyNode[], redContext: boolean) {
|
||||
for (const node of nodes) {
|
||||
if (node.type === "text") {
|
||||
buffer += node.data;
|
||||
if (redContext) {
|
||||
bufferIsRed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (node.type !== "tag") {
|
||||
continue;
|
||||
}
|
||||
if (node.name === "br") {
|
||||
flush();
|
||||
continue;
|
||||
}
|
||||
if (isRedSpan(node)) {
|
||||
walk(node.children, true);
|
||||
continue;
|
||||
}
|
||||
walk(node.children, redContext);
|
||||
}
|
||||
}
|
||||
if (cellEl.type === "tag") {
|
||||
walk(cellEl.children, false);
|
||||
}
|
||||
flush();
|
||||
return titles;
|
||||
}
|
||||
|
||||
async function fetchSheet(url: string) {
|
||||
log.info(`Fetching ${url}`);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status} fetching ${url}`);
|
||||
}
|
||||
return res.text();
|
||||
}
|
||||
|
||||
function cleanTierName(text: string) {
|
||||
return text.trim().replaceAll("+", "+");
|
||||
}
|
||||
|
||||
// L12 layout: thead shim, then one tier-header row + one data row in tbody.
|
||||
// Each data <td> holds <br>-separated titles; red <span>s mark 個人差.
|
||||
function extractTiersColumnwise(html: string) {
|
||||
const $ = cheerio.load(html);
|
||||
const rows = $("tr").toArray();
|
||||
if (rows.length < 3) {
|
||||
throw new Error(`Expected at least 3 rows, got ${rows.length}`);
|
||||
}
|
||||
|
||||
const headerCells = $(rows[1]).find("td").toArray();
|
||||
const tierCells = $(rows[2]).find("td").toArray();
|
||||
|
||||
const colCount = Math.min(headerCells.length, tierCells.length);
|
||||
if (headerCells.length !== tierCells.length) {
|
||||
log.info(
|
||||
`Warning: header has ${headerCells.length} cols, data has ${tierCells.length} cols`,
|
||||
);
|
||||
}
|
||||
|
||||
const tiers = [];
|
||||
for (let k = 0; k < colCount; k++) {
|
||||
const tierName = cleanTierName($(headerCells[k]).text());
|
||||
const songsInTier = parseCell(tierCells[k]);
|
||||
tiers.push({ tier: tierName, songsInTier });
|
||||
}
|
||||
return tiers;
|
||||
}
|
||||
|
||||
// L11 layout: thead shim, then in tbody: title row, tier-header row, then one
|
||||
// song per cell per row. Empty cell = tier ran out of songs. 個人差 cells
|
||||
// carry class "s5" on the <td>.
|
||||
function extractTiersRowwise(html: string) {
|
||||
const $ = cheerio.load(html);
|
||||
const rows = $("tr").toArray();
|
||||
if (rows.length < 4) {
|
||||
throw new Error(`Expected at least 4 rows, got ${rows.length}`);
|
||||
}
|
||||
|
||||
const headerCells = $(rows[2]).find("td").toArray();
|
||||
const tiers = headerCells.map((c) => ({
|
||||
tier: cleanTierName($(c).text()),
|
||||
songsInTier: [] as songInTier[],
|
||||
}));
|
||||
|
||||
for (let k = 3; k < rows.length; k++) {
|
||||
const cells = $(rows[k]).find("td").toArray();
|
||||
const colCount = Math.min(tiers.length, cells.length);
|
||||
for (let col = 0; col < colCount; col++) {
|
||||
const cell = $(cells[col]);
|
||||
const text = cell.text().trim();
|
||||
if (text.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const cls = cell.attr("class") || "";
|
||||
const kojinsa = cls.split(/\s+/).includes(KOJINSA_CLASS);
|
||||
const { title, difficulty } = parseTitleAndDiff(text);
|
||||
tiers[col].songsInTier.push({ title: title, difficulty: difficulty, kojinsa: kojinsa });
|
||||
}
|
||||
}
|
||||
return tiers;
|
||||
}
|
||||
|
||||
function applyTiers(
|
||||
chartsData: SEEDS_ChartDocument<"iidx-sp">[],
|
||||
songsData: SEEDS_SongDocument<"iidx">[],
|
||||
level: number,
|
||||
tiers: {
|
||||
tier: string;
|
||||
songsInTier: songInTier[];
|
||||
}[],
|
||||
field: "hcTier" | "ncTier",
|
||||
) {
|
||||
let updated = 0;
|
||||
let notFound = 0;
|
||||
|
||||
function tierToValue(tier: string) {
|
||||
switch (tier) {
|
||||
case "S+":
|
||||
return level + 1.2;
|
||||
case "S":
|
||||
return level + 1.0;
|
||||
case "A+":
|
||||
return level + 0.9;
|
||||
case "A":
|
||||
return level + 0.8;
|
||||
case "B+":
|
||||
return level + 0.7;
|
||||
case "B":
|
||||
return level + 0.6;
|
||||
case "C":
|
||||
return level + 0.4;
|
||||
case "D":
|
||||
return level + 0.2;
|
||||
case "E":
|
||||
return level + 0.0;
|
||||
case "F":
|
||||
return level - 0.2;
|
||||
default:
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
function isInAc(versions: string[]) {
|
||||
for (const version of ["33", "32", "31", "30"]) {
|
||||
if (versions.includes(version)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const { tier, songsInTier } of tiers) {
|
||||
if (!tier || tier === "未定" || tier === "超個人差") {
|
||||
continue;
|
||||
}
|
||||
for (const { title, difficulty, kojinsa } of songsInTier) {
|
||||
const songMatch = findMatchingSong(title, songsData);
|
||||
if (!songMatch) {
|
||||
log.warn(`No match: ${title}`);
|
||||
notFound++;
|
||||
continue;
|
||||
}
|
||||
let chartFound = false;
|
||||
for (const chart of chartsData) {
|
||||
if (
|
||||
chart.songID === songMatch.id &&
|
||||
chart.difficulty === difficulty &&
|
||||
chart.data["2dxtraSet"] === null &&
|
||||
isInAc(chart.versions)
|
||||
) {
|
||||
chart.data[field] = {
|
||||
individualDifference: kojinsa,
|
||||
text: level + tier,
|
||||
value: tierToValue(tier),
|
||||
};
|
||||
updated++;
|
||||
chartFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!chartFound) {
|
||||
log.warn(`Chart not found: ${songMatch.title} (SP ${difficulty})`);
|
||||
notFound++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { updated, notFound };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const chartsData = ReadCollection("charts-iidx-sp.json");
|
||||
const songsData = ReadCollection("songs-iidx.json");
|
||||
|
||||
for (const config of TIER_CONFIGS) {
|
||||
log.info(`Processing L${config.level} ${config.gauge}`);
|
||||
const html = await fetchSheet(config.url);
|
||||
const tiers =
|
||||
config.format === "rowwise" ? extractTiersRowwise(html) : extractTiersColumnwise(html);
|
||||
log.info(`Parsed ${tiers.length} tiers: ${tiers.map((t) => t.tier).join(", ")}`);
|
||||
const { updated, notFound } = applyTiers(
|
||||
chartsData,
|
||||
songsData,
|
||||
config.level,
|
||||
tiers,
|
||||
config.gauge === "normal" ? "ncTier" : "hcTier",
|
||||
);
|
||||
log.info(`L${config.level} ${config.gauge}: ${updated} updated, ${notFound} not found`);
|
||||
}
|
||||
WriteCollection("charts-iidx-sp.json", chartsData);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(`ERROR: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -26,4 +26,13 @@ export const EXT_PINKYCRUSH = [
|
||||
"2025082500",
|
||||
];
|
||||
|
||||
export const EXT_SPARKLE_SHOWER = [
|
||||
"2026022400",
|
||||
"2026031000",
|
||||
"2026033100",
|
||||
"2026042200",
|
||||
"2026052600",
|
||||
"2026061600",
|
||||
];
|
||||
|
||||
export const MODEL_SDVX3_KONASTE = "QCV";
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
EXT_PINKYCRUSH,
|
||||
EXT_RESIDENT,
|
||||
EXT_ROOTAGE,
|
||||
EXT_SPARKLE_SHOWER,
|
||||
MODEL_IIDX,
|
||||
MODEL_IIDX_LIGHTNING,
|
||||
MODEL_INFINITAS_2,
|
||||
@@ -157,6 +158,10 @@ export function SoftwareIDToVersion(model: string, log: KtLogger): Versions[Game
|
||||
} else if (data.rev === REV_OMNIMIX) {
|
||||
return "32-omni";
|
||||
}
|
||||
} else if (EXT_SPARKLE_SHOWER.includes(data.ext)) {
|
||||
if (data.rev === REV_NORMAL) {
|
||||
return "33";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user