Merge pull request 'add sound voltex support' (#1) from sdvx into eacnet

Reviewed-on: https://git.girlsband.party/laochan/laochan-eacnet/pulls/1
This commit is contained in:
geekidos
2024-04-20 11:58:39 +00:00
35 changed files with 20076 additions and 7860 deletions
+5
View File
@@ -29,3 +29,8 @@ build/
.DS_Store
.env
input.xml
output.json
dump.xml
pdata.json
+465 -11
View File
@@ -17,6 +17,7 @@
"dedent-js": "^1.0.1",
"dotenv": "^16.4.5",
"fast-xml-parser": "^4.3.2",
"highwayhash": "^3.1.1",
"koa": "^2.15.0",
"koa-bodyparser": "^4.4.1",
"lodash": "^4.17.21",
@@ -2522,7 +2523,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -2555,6 +2555,11 @@
"node": ">= 8"
}
},
"node_modules/aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"node_modules/arc4": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/arc4/-/arc4-2.1.4.tgz",
@@ -2563,6 +2568,15 @@
"node": ">=0.10"
}
},
"node_modules/are-we-there-yet": {
"version": "1.1.7",
"resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz",
@@ -2729,6 +2743,34 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/bl/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2812,6 +2854,15 @@
"node": ">=16.20.1"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -2924,6 +2975,11 @@
"is-regex": "^1.0.3"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -2979,6 +3035,14 @@
"type-is": "^1.6.16"
}
},
"node_modules/code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/collect-v8-coverage": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
@@ -3009,6 +3073,11 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
},
"node_modules/constantinople": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/constantinople/-/constantinople-4.0.1.tgz",
@@ -3069,6 +3138,11 @@
"cosmokit": "^1.5.2"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/cosmokit": {
"version": "1.5.2",
"resolved": "https://registry.npmmirror.com/cosmokit/-/cosmokit-1.5.2.tgz",
@@ -3133,6 +3207,17 @@
}
}
},
"node_modules/decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"dependencies": {
"mimic-response": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/dedent": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
@@ -3157,6 +3242,14 @@
"resolved": "https://registry.npmmirror.com/deep-equal/-/deep-equal-1.0.1.tgz",
"integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw=="
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3207,6 +3300,17 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -3305,8 +3409,7 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
@@ -3316,6 +3419,14 @@
"node": ">= 0.8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -3754,6 +3865,14 @@
"node": ">= 0.8.0"
}
},
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"engines": {
"node": ">=6"
}
},
"node_modules/expect": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
@@ -3956,6 +4075,11 @@
"node": ">= 0.6"
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3984,6 +4108,64 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmmirror.com/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
"dependencies": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
}
},
"node_modules/gauge/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gauge/node_modules/is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
"dependencies": {
"number-is-nan": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gauge/node_modules/string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
"dependencies": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gauge/node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4047,6 +4229,11 @@
"resolve-pkg-maps": "^1.0.0"
}
},
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -4177,6 +4364,11 @@
"node": ">= 0.4"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -4188,6 +4380,19 @@
"node": ">= 0.4"
}
},
"node_modules/highwayhash": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/highwayhash/-/highwayhash-3.1.1.tgz",
"integrity": "sha512-FjsF/X47Q9+y7TJ+xjZxcNaOGhrGJTg5AFedEpNYate5GB5ezXeG3tqMjsuZ99R2If9xDdQQRCrka6hRC7J82g==",
"hasInstallScript": true,
"dependencies": {
"node-addon-api": "^3.1.0",
"prebuild-install": "^6.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -4249,6 +4454,11 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"node_modules/ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
@@ -4325,6 +4535,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -4375,7 +4590,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -4459,6 +4673,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -5452,6 +5671,14 @@
"node": ">=6"
}
},
"node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"engines": {
"node": ">=8"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -5464,6 +5691,11 @@
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"node_modules/minipass": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
@@ -5473,6 +5705,11 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/mongodb": {
"version": "6.4.0",
"resolved": "https://registry.npmmirror.com/mongodb/-/mongodb-6.4.0.tgz",
@@ -5532,6 +5769,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -5546,6 +5788,27 @@
"node": ">= 0.6"
}
},
"node_modules/node-abi": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-2.30.1.tgz",
"integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==",
"dependencies": {
"semver": "^5.4.1"
}
},
"node_modules/node-abi/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/node-addon-api": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-3.2.1.tgz",
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
},
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -5579,6 +5842,25 @@
"node": ">=8"
}
},
"node_modules/npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dependencies": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"node_modules/number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
@@ -5607,7 +5889,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -5888,6 +6169,32 @@
"node": ">=8"
}
},
"node_modules/prebuild-install": {
"version": "6.1.4",
"resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-6.1.4.tgz",
"integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==",
"dependencies": {
"detect-libc": "^1.0.3",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^2.21.0",
"npmlog": "^4.0.1",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^3.0.3",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -5938,6 +6245,11 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/promise": {
"version": "7.3.1",
"resolved": "https://registry.npmmirror.com/promise/-/promise-7.3.1.tgz",
@@ -6071,6 +6383,15 @@
"resolved": "https://registry.npmmirror.com/pug-walk/-/pug-walk-2.0.0.tgz",
"integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ=="
},
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -6163,12 +6484,53 @@
"node": ">= 0.8"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/rc/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/reflect-metadata": {
"version": "0.2.1",
"resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
@@ -6405,6 +6767,11 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -6464,8 +6831,22 @@
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"node_modules/simple-get": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
@@ -6544,6 +6925,19 @@
"node": ">= 0.6"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
@@ -6561,7 +6955,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -6590,7 +6983,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -6669,6 +7061,45 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tar-stream/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -6908,6 +7339,17 @@
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -7019,6 +7461,11 @@
"punycode": "^2.1.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
@@ -7101,6 +7548,14 @@
"node": ">= 8"
}
},
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/with": {
"version": "7.0.2",
"resolved": "https://registry.npmmirror.com/with/-/with-7.0.2.tgz",
@@ -7153,8 +7608,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/write-file-atomic": {
"version": "4.0.2",
+3 -1
View File
@@ -40,7 +40,8 @@
"lint:fix": "eslint . --ext .ts --ext .mts --fix",
"test": "jest --coverage",
"prettier": "prettier --config .prettierrc --write .",
"test:watch": "jest --watch"
"test:watch": "jest --watch",
"xmltojson": "tsx src/tool/xml-to-json.ts"
},
"author": "GEEKiDoS",
"license": "MIT",
@@ -53,6 +54,7 @@
"dedent-js": "^1.0.1",
"dotenv": "^16.4.5",
"fast-xml-parser": "^4.3.2",
"highwayhash": "^3.1.1",
"koa": "^2.15.0",
"koa-bodyparser": "^4.4.1",
"lodash": "^4.17.21",
+2 -2
View File
@@ -2,8 +2,8 @@ import { Logger } from "@cordisjs/logger";
import Router from "@koa/router";
import { Context } from "../index.js";
import { UserService } from "../../services/p2d/user.js";
import { PlayerCustomizeSetting, PlayerRivalData } from "../../types/index.js";
import { RivalPatch, RivalPostOrDelete } from "../../types/api.js";
import { PlayerCustomizeSetting, PlayerRivalData } from "../../types/p2d/index.js";
import { RivalPatch, RivalPostOrDelete } from "../../types/p2d/api.js";
const logger = new Logger('p2d-api');
const router = new Router({
+38 -12
View File
@@ -1,22 +1,48 @@
import { Context } from '../types.js';
import { eacnet } from '../decorators/eacnet.js';
import { p2d, sdvx } from '../decorators/eacnet.js';
import { v } from '../utils/kxml-value.js';
import { singleton } from 'tsyringe';
@singleton()
export class DefaultService {
async default(ctx: Context) {
const descriptor = {
value: () => ({
status: v.s32(0),
error: v.s32(0),
result: {
dummy: v.u8(0),
}
})
};
if (ctx.service.name === 'p2d') {
const descriptor = {
value: () => ({
status: v.s32(0),
error: v.s32(0),
result: {},
})
};
eacnet(ctx.service.name)(undefined, undefined, descriptor);
return descriptor.value();
p2d()(undefined, undefined, descriptor);
return descriptor.value();
} else if (ctx.service.name === 'sdvx') {
const result = {
status: v.s32(0),
error_code: v.s32(0),
response: {},
};
// handle ac relay
if (ctx.eacnetRequest) {
result['xrpc_status_code'] = v.s32(0);
result['xrpc_fault_code'] = v.s32(0);
result['response'] = {
[ctx.eacnetRequest.module]: {
$status: 0,
}
}
}
const descriptor = {
value: () => result,
};
sdvx()(undefined, undefined, descriptor);
return descriptor.value();
}
throw new Error(`unsupported game ${ctx.service.name}`);
}
}
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,9 +1,9 @@
import { eacnet } from "../../decorators/eacnet.js";
import { p2d } from "../../decorators/eacnet.js";
import { Context } from "../../types.js";
import { v } from "../../utils/kxml-value.js";
export class Game {
@eacnet('p2d')
@p2d()
async getChampionshipInfo() {
return {
status: v.s32(0),
@@ -15,7 +15,7 @@ export class Game {
entry_begin_clock: v.u64(0),
championship_begin_clock: v.u64(0),
championship_end_clock: v.u64(0),
is_possible_play: v.bool(0),
is_possible_play: v.bool(false),
repertoire_list: {
repertoire_num: v.s32(0),
}
@@ -23,7 +23,7 @@ export class Game {
}
}
@eacnet('p2d')
@p2d()
async getServerValues() {
return {
status: v.s32(0),
@@ -41,7 +41,7 @@ export class Game {
}
}
@eacnet('p2d')
@p2d()
async checkGameVersion() {
return {
status: v.s32(0),
@@ -52,7 +52,7 @@ export class Game {
};
}
@eacnet('p2d')
@p2d()
async getClearRate(ctx: Context) {
const { play_style } = ctx.body as { play_style: number };
return {
@@ -63,7 +63,7 @@ export class Game {
}
}
@eacnet('p2d')
@p2d()
async getGoodsList() {
const goods = [{
goods_id: v.str('G1000000'),
+2 -23
View File
@@ -2,32 +2,11 @@ import { v } from "../../utils/kxml-value.js";
import { Game } from "./game.js";
import { Service } from "./service.js";
import { User } from "./user.js";
import data from './data.json' with { type: "json" };
import { inject, singleton } from "tsyringe";
import { UserService } from "../../services/p2d/user.js";
import { Music } from "./music.js";
function Combine(...modules: ({ new(): any })[]) {
class Modules {
constructor() {
modules.forEach(v => Object.assign(this, new v()));
}
}
modules.forEach(v => {
const descriptors = Object.getOwnPropertyDescriptors(v.prototype);
for (const name in descriptors) {
if (name === 'constructor')
continue;
const descriptor = descriptors[name];
Object.defineProperty(Modules.prototype, name, descriptor);
}
});
return Modules;
}
import { Combine } from "../../utils/combine.js";
import * as data from '../../datas/p2d.js'
@singleton()
export default class extends Combine(Service, User, Game, Music) {
+9 -9
View File
@@ -1,11 +1,11 @@
import { PlayerPlayLog } from "../../types/index.js";
import { eacnet } from "../../decorators/eacnet.js";
import { PlayerPlayLog } from "../../types/p2d/index.js";
import { p2d } from "../../decorators/eacnet.js";
import { UserService } from "../../services/p2d/user.js";
import { Context } from "../../types.js";
import { v } from "../../utils/kxml-value.js";
import { MUSIC_LIST } from "./index.js";
import { Binary } from "mongodb";
import data from './data.json' with { type: "json" };
import * as data from '../../datas/p2d.js'
// user only needs to unlock omni music, which is temporary
const userUnlockMusic = new Map<string, number>();
@@ -13,7 +13,7 @@ const userUnlockMusic = new Map<string, number>();
export class Music {
userService: UserService;
@eacnet('p2d')
@p2d()
async getMusicGhost(ctx: Context) {
// playstyle is actually chart diff, game is sending wrong request
// TODO: check game code and maybe patch it later
@@ -62,7 +62,7 @@ export class Music {
ghost_data: v.bin(Buffer.from(playlog.ghost.buffer)),
dj_name: v.str(djname),
best_option: {
valid: v.bool(1),
valid: v.bool(true),
arrange_0: v.s32(playlog.arrange_0),
arrange_1: v.s32(playlog.arrange_1),
assist: v.s32(playlog.assist),
@@ -85,7 +85,7 @@ export class Music {
};
}
@eacnet('p2d')
@p2d()
async reportMusicResult(ctx: Context) {
const result = ctx.body as PlayerPlayLog;
result.player = ctx.token;
@@ -131,7 +131,7 @@ export class Music {
};
}
@eacnet('p2d')
@p2d()
async unlockMusic(ctx: Context) {
const { music: { music_id } } = ctx.body as { music: { music_id: number } };
userUnlockMusic.set(ctx.token, music_id);
@@ -144,7 +144,7 @@ export class Music {
};
}
@eacnet('p2d')
@p2d()
async getMusicList(ctx: Context) {
let music = [...MUSIC_LIST];
@@ -177,7 +177,7 @@ export class Music {
};
}
@eacnet('p2d')
@p2d()
async getMusicData(ctx: Context) {
const { play_style } = ctx.body as { play_style: number };
+7 -7
View File
@@ -1,10 +1,10 @@
import { eacnet } from '../../decorators/eacnet.js';
import { p2d } from '../../decorators/eacnet.js';
import { Context } from '../../types.js';
import config from '../../utils/config.js';
import { v } from '../../utils/kxml-value.js';
export class Service {
@eacnet('p2d')
@p2d()
async sendLog() {
return {
status: v.s32(0),
@@ -12,7 +12,7 @@ export class Service {
};
}
@eacnet('p2d')
@p2d()
async getServerState() {
return {
status: v.s32(0),
@@ -25,7 +25,7 @@ export class Service {
}
}
@eacnet('p2d')
@p2d()
async getServerClock() {
return {
status: v.s32(0),
@@ -36,7 +36,7 @@ export class Service {
}
}
@eacnet('p2d')
@p2d()
async heartbeat(ctx: Context) {
return {
status: v.s32(0),
@@ -51,7 +51,7 @@ export class Service {
};
}
@eacnet('p2d')
@p2d()
async checkSendLogAvailable() {
return {
status: v.s32(0),
@@ -63,7 +63,7 @@ export class Service {
};
}
@eacnet('p2d')
@p2d()
async getServices() {
return {
status: v.s32(0),
+20 -20
View File
@@ -1,7 +1,7 @@
import { eacnet } from "../../decorators/eacnet.js";
import { p2d } from "../../decorators/eacnet.js";
import { Context } from "../../types.js";
import { v } from "../../utils/kxml-value.js";
import { fromToken } from "../../utils/laochan-id.js";
import { tokenToInfinitasId } from "../../utils/laochan-id.js";
import { UserService } from "../../services/p2d/user.js";
import { sha256 } from "../../utils/sha256.js";
import { toKBinXml } from "../../utils/kbinxml.js";
@@ -10,7 +10,7 @@ import { ITEM_LIST } from "./index.js";
export class User {
userService: UserService;
@eacnet('p2d')
@p2d()
async addPoint() {
return {
status: v.s32(0),
@@ -18,7 +18,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async reserveChangePoint() {
return {
status: v.s32(0),
@@ -26,7 +26,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async sendGradeCertificationLog(ctx: Context) {
await this.userService.addCourseLog({
player: ctx.token,
@@ -39,7 +39,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async consumeItem(ctx: Context) {
const { item_id } = ctx.body;
const { items_count } = await this.userService.getCustomizeSetting(ctx.token);
@@ -75,7 +75,7 @@ export class User {
};
}
@eacnet('p2d')
@p2d()
async reserveConsumeItem() {
return {
status: v.s32(0),
@@ -83,7 +83,7 @@ export class User {
};
}
@eacnet('p2d')
@p2d()
async getCustomizeSetting(ctx: Context) {
const { customize, other_customize } = await this.userService.getCustomizeSetting(ctx.token);
@@ -103,7 +103,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async gameEnd() {
return {
status: v.s32(0),
@@ -111,7 +111,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async getPointList(ctx: Context) {
const customize = await this.userService.getCustomizeSetting(ctx.token);
@@ -128,7 +128,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async getPrivilegeClient() {
return {
status: v.s32(0),
@@ -139,7 +139,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async getPrivilegeServer() {
return {
status: v.s32(0),
@@ -150,7 +150,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async savePlayData(ctx: Context) {
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
const localChecksum = sha256(pdata);
@@ -167,7 +167,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async getPlayData(ctx: Context) {
const result = await this.userService.getPDataBinary(ctx.token);
@@ -190,7 +190,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async registPlayer(ctx: Context) {
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
const localChecksum = sha256(pdata);
@@ -207,12 +207,12 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async checkPlayData(ctx: Context) {
const { check_sum } = ctx.body as { check_sum: string };
const checksum = await this.userService.getPDataChecksum(ctx.token);
const id = fromToken(ctx.token);
const id = tokenToInfinitasId(ctx.token);
if (!checksum) {
return {
@@ -249,7 +249,7 @@ export class User {
};
}
@eacnet('p2d')
@p2d()
async getRivalInfo(ctx: Context) {
const rivalData = await this.userService.getPlayerRivalData(ctx.token);
if (!rivalData.enabled) {
@@ -320,7 +320,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async getCompeScoreData() {
return {
status: v.s32(0),
@@ -329,7 +329,7 @@ export class User {
}
}
@eacnet('p2d')
@p2d()
async getItemList(ctx: Context) {
const { items_count } = await this.userService.getCustomizeSetting(ctx.token);
+134
View File
@@ -0,0 +1,134 @@
import { sdvx } from "../../decorators/eacnet.js";
import { Context } from "../../types.js";
import { Serializable, v } from "../../utils/kxml-value.js";
import * as data from '../../datas/sdvx.js';
export class AcRelay {
@sdvx()
async sv6_play_e(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
},
}
};
}
@sdvx()
async sv6_play_s(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
play_id: v.u32(Math.floor(new Date().valueOf() / 6e4)),
},
}
};
}
@sdvx()
async sv6_hiscore() {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
sc: {},
}
}
};
}
@sdvx()
async sv6_log(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
}
}
};
}
@sdvx()
async sv6_common(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
music: {},
event: {
info: data.eventList.map(id => ({
event_id: v.str(id),
}))
},
extend: {
info: data.extendInfos,
},
music_limited: {
info: data.musicInfos,
},
skill_course: {
info: data.skillCourses,
},
appealcard: {},
valgene: {},
}
}
}
}
@sdvx()
async sv6_music_url(ctx: Context): Promise<Serializable> {
let { music_id } = ctx.body as { music_id: number | number[] };
if (typeof music_id === 'number') {
music_id = [music_id];
}
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
music: music_id.map(id => {
const music = data.musicDownloadLinks[id];
if (!music) {
return {};
}
return {
music_id: v.s32(id),
url: v.str(music.url),
hash: v.str(music.hash),
size: v.s32(music.size),
}
}),
}
}
};
}
}
+158
View File
@@ -0,0 +1,158 @@
import { sdvx } from "../../decorators/eacnet.js";
import { Combine } from "../../utils/combine.js";
import config from "../../utils/config.js";
import { Serializable, v } from "../../utils/kxml-value.js";
import { AcRelay } from "./ac-relay.js";
import { UserService } from "../../services/sdvx/user.js";
import { inject, singleton } from "tsyringe";
import { User } from "./user.js";
import { Music } from "./music.js";
@singleton()
export default class extends Combine(AcRelay, User, Music) {
constructor(
@inject(UserService) private readonly userService: UserService,
) {
super();
this.userService;
}
@sdvx()
async heartbeat(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
state: v.s32(1),
next_clock: v.u64(new Date().valueOf() + 36e5),
server_state: v.s32(1),
mainte_start_clock: v.u64(0),
mainte_end_clock: v.u64(0),
usta_boot_status: v.s32(1),
usta_time_remain: v.s32(0),
}
};
}
@sdvx()
async checkVersion(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
permission: v.s32(1),
}
};
}
@sdvx()
async getServerClock(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
server_clock: v.u64(new Date().valueOf()),
}
}
}
@sdvx()
async getServerState(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
server_state: v.s32(1),
mainte_start_clock: v.u64(0),
mainte_end_clock: v.u64(0),
}
}
}
@sdvx('SHIFT_JIS')
async getServices(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
service_num: v.s32(18),
service: [
{
service_name: v.str('acRelay'),
url: v.str(`${config.selfUrl}/need_auth/AcRelay`),
},
{
service_name: v.str('cancelReserveConsumeItem'),
url: v.str(`${config.selfUrl}/need_auth/CancelReserveConsumeItem`),
},
{
service_name: v.str('checkGameStart'),
url: v.str(`${config.selfUrl}/need_auth/CheckGameStart`),
},
{
service_name: v.str('checkVersion'),
url: v.str(`${config.selfUrl}/needless_auth/CheckVersion`),
},
{
service_name: v.str('consumeItem'),
url: v.str(`${config.selfUrl}/need_auth/ConsumeItem`),
},
{
service_name: v.str('getGoodsList'),
url: v.str(`${config.selfUrl}/needless_auth/GetGoodsList`),
},
{
service_name: v.str('getHash'),
url: v.str(`${config.selfUrl}/needless_auth/GetHash`),
},
{
service_name: v.str('getItemList'),
url: v.str(`${config.selfUrl}/need_auth/GetItemList`),
},
{
service_name: v.str('getLauncherData'),
url: v.str(`${config.selfUrl}/needless_auth/GetLauncherData`),
},
{
service_name: v.str('getResourceInfo'),
url: v.str(`${config.selfUrl}/needless_auth/GetResourceInfo`),
},
{
service_name: v.str('getServerClock'),
url: v.str(`${config.selfUrl}/pre_process/GetServerClock`),
},
{
service_name: v.str('getServerState'),
url: v.str(`${config.selfUrl}/pre_process/GetServerStatus`),
},
{
service_name: v.str('getServices'),
url: v.str(`${config.selfUrl}/pre_process/GetServices`),
},
{
service_name: v.str('getSubscriptionStatus'),
url: v.str(`${config.selfUrl}/need_auth/GetSubscriptionStatus`),
},
{
service_name: v.str('getUserIDs'),
url: v.str(`${config.selfUrl}/need_auth/GetUserId`),
},
{
service_name: v.str('heartbeat'),
url: v.str(`${config.selfUrl}/need_auth/Heartbeat`),
},
{
service_name: v.str('reserveConsumeItem'),
url: v.str(`${config.selfUrl}/need_auth/ReserveConsumeItem`),
},
{
service_name: v.str('uploadFile'),
url: v.str(`${config.selfUrl}/needless_auth/UploadFile`),
},
],
}
};
}
}
+126
View File
@@ -0,0 +1,126 @@
import { sdvx } from "../../decorators/eacnet.js";
import { UserService } from "../../services/sdvx/user.js";
import { Serializable, v } from "../../utils/kxml-value.js";
import { Context } from "../../types.js";
import { PlayerMusicData, PlayerPlayLog } from "../../types/sdvx/index.js";
export class Music {
userService: UserService;
@sdvx()
async sv6_save_m(ctx: Context): Promise<Serializable> {
const { track } = ctx.body as { track: PlayerPlayLog };
track.player = ctx.token;
track.play_time = new Date();
const mdata = (await this.userService.getPlayerMusicData(ctx.token, track.music_id, track.music_type)) ?? {
player: ctx.token,
music_id: track.music_id,
music_type: track.music_type,
score: 0, exscore: 0, clear_type: 0, score_grade: 0,
max_chain: 0, play_count: 0,
btn_rate: 0, long_rate: 0, vol_rate: 0,
best_play_time: track.play_time,
} as PlayerMusicData;
if (track.score > mdata.score) {
mdata.score = track.score;
mdata.score_grade = track.score_grade;
mdata.best_play_time = track.play_time;
mdata.btn_rate = track.btn_rate;
mdata.long_rate = track.long_rate;
mdata.vol_rate = track.vol_rate;
}
if (track.exscore > mdata.score) {
mdata.exscore = track.exscore;
// if score is same, use exscore to compare
if (track.score == mdata.score) {
mdata.best_play_time = track.play_time;
mdata.btn_rate = track.btn_rate;
mdata.long_rate = track.long_rate;
mdata.vol_rate = track.vol_rate;
}
}
if (track.clear_type > mdata.clear_type) {
mdata.clear_type = track.clear_type;
}
if (track.max_chain > mdata.max_chain) {
mdata.max_chain = track.max_chain;
}
mdata.play_count++;
await Promise.all([
this.userService.insertPlayLog(track),
this.userService.upsertMusicData(mdata),
]);
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
}
}
};
}
@sdvx()
async sv6_load_r(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
}
}
};
}
/*
* music_id diff
* score exscore clear_type score_grade max_chain play_count btn_rate long_rate vol_rate
* 0 0 0 0 0 timestamp_s 0 0 0 0 0 0
*/
@sdvx()
async sv6_load_m(ctx: Context): Promise<Serializable> {
const mdata = await this.userService.getPlayerMusicDatas(ctx.token);
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
music: {
info: mdata.map(d => ({
date: d.best_play_time,
param: v.u32([
d.music_id, d.music_type,
d.score, d.exscore, d.clear_type, d.score_grade, d.max_chain,
d.play_count, d.btn_rate, d.long_rate, d.vol_rate,
0, 0, 0, 0, 0,
Math.floor(d.best_play_time.valueOf() / 1000),
0, 0, 0, 0, 0, 0,
])
}))
},
}
}
};
}
}
+284
View File
@@ -0,0 +1,284 @@
import * as data from '../../datas/sdvx.js';
import { tokenToCardNumber, tokenToHash, tokenToSnsId } from "../../utils/laochan-id.js";
import { Context } from "../../types.js";
import { sdvx } from '../../decorators/eacnet.js';
import { KValueG, Serializable, ValueTypes, v } from '../../utils/kxml-value.js';
import { UserService } from '../../services/sdvx/user.js';
import { Item, Param } from '../../types/sdvx/savedata.js';
import { dateToString } from '../../utils/time.js';
function trySet<T>(kvalue: KValueG<ValueTypes, T>, value: T) {
if (value === undefined || value === null)
return;
kvalue.__value = value;
}
function tryAdd(kvalue: KValueG<ValueTypes, number>, value: number) {
if (value === undefined || value === null)
return;
if (kvalue.__value instanceof Array)
return;
kvalue.__value += value;
}
export function tryMergeItem(params: Item[], newParams: object[]) {
if (!params) {
params = [];
}
if (!newParams) {
return params;
}
if (!(newParams instanceof Array)) {
newParams = [newParams];
}
for (const np of newParams) {
const target = params.find(p => p.id.__value === np['id'] && p.type.__value === np['type']);
if (target) {
target.type = v.u8(np['type']);
target.param = v.u32(np['param']);
continue;
}
params.push({
type: v.u8(np['type']),
id: v.u32(np['id']),
param: v.u32(np['param']),
})
}
return params;
}
export function tryMergeParam(params: Param[], newParams: object[]) {
if (!params) {
params = [];
}
if (!newParams) {
return params;
}
if (!(newParams instanceof Array)) {
newParams = [newParams];
}
for (const np of newParams) {
const target = params.find(p => p.id.__value === np['id'] && p.type.__value === np['type']);
if (target) {
target.param = v.s32(np['param']);
continue;
}
params.push({
type: v.s32(np['type']),
id: v.s32(np['id']),
param: v.s32(np['param']),
})
}
return params;
}
export class User {
userService: UserService;
@sdvx()
async sv6_save_c(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
play_id: v.u32(Math.floor(new Date().valueOf() / 6e4)),
},
}
};
}
@sdvx()
async sv6_save(ctx: Context): Promise<Serializable> {
const playData = await this.userService.getPlayerData(ctx.token);
if (!playData) {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(1),
xrpc_fault_code: v.s32(404),
};
}
const newData = ctx.body;
trySet(playData.appeal_id, newData.appeal_id);
trySet(playData.skill_level, newData.skill_level);
trySet(playData.skill_base_id, newData.skill_base_id);
trySet(playData.skill_name_id, newData.skill_name_id);
tryAdd(playData.blaster_energy, newData.earned_blaster_energy);
tryAdd(playData.gamecoin_packet, newData.earned_gamecoin_packet);
tryAdd(playData.gamecoin_block, newData.earned_gamecoin_block);
tryAdd(playData.play_count, 1);
trySet(playData.hispeed, newData.hispeed);
trySet(playData.lanespeed, newData.lanespeed);
trySet(playData.gauge_option, newData.gauge_option);
trySet(playData.ars_option, newData.ars_option);
trySet(playData.notes_option, newData.notes_option);
trySet(playData.early_late_disp, newData.early_late_disp);
trySet(playData.draw_adjust, newData.draw_adjust);
trySet(playData.eff_c_left, newData.eff_c_left);
trySet(playData.eff_c_right, newData.eff_c_right);
trySet(playData.last_music_id, newData.music_id);
trySet(playData.last_music_type, newData.music_type);
trySet(playData.sort_type, newData.sort_type);
trySet(playData.narrow_down, newData.narrow_down);
trySet(playData.headphone, newData.headphone);
playData.item.info = tryMergeItem(playData.item.info, newData.item?.info);
playData.item_infinite.info = tryMergeItem(playData.item_infinite.info, newData.item_infinite?.info);
playData.param.info = tryMergeParam(playData.param.info, newData.param?.info);
playData.last_date = v.str(dateToString(new Date()));
if (!await this.userService.savePlayerData(ctx.token, playData)) {
ctx.logger.error('play data save failed');
}
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: { $status: 0 },
}
};
}
@sdvx()
async sv6_new(ctx: Context): Promise<Serializable> {
await this.userService.createEmptyPlayerData(ctx.token, ctx.body.name);
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
}
}
};
}
@sdvx()
async sv6_load(ctx: Context): Promise<Serializable> {
const playData = await this.userService.getPlayerData(ctx.token);
if (!playData) {
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
result: v.u8(1),
}
}
}
}
// blaster pass
playData.ea_shop.blaster_pass_enable = v.bool(true);
playData.ea_shop.blaster_pass_limit_date = v.u64(new Date().valueOf() + (30 * 60 * 60 * 1000));
// unlock all navigaters
playData.item.info = tryMergeItem(playData.item.info, data.charaItems);
playData.start_date = v.str(dateToString(new Date()));
return {
status: v.s32(0),
error_code: v.s32(0),
xrpc_status_code: v.s32(0),
xrpc_fault_code: v.s32(0),
response: {
game: {
$status: 0,
result: v.u8(0),
...playData,
}
}
};
}
@sdvx()
async getItemList(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
item_num: v.s32(data.itemList.length),
item: data.itemList.map(id => ({
item_id: v.str(id),
not_free_count: v.s32(1),
free_count: v.s32(0),
})),
}
};
}
@sdvx()
async getGoodsList(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
goods_num: v.s32(0),
}
};
}
@sdvx()
async getUserIDs(ctx: Context): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
card_num: v.str(tokenToCardNumber(ctx.token)),
ref_id: v.str(tokenToHash(ctx.token)),
data_id: v.str(tokenToHash(ctx.token)),
sns_id: v.str(tokenToSnsId(ctx.token)),
}
};
}
@sdvx()
async getSubscriptionStatus(): Promise<Serializable> {
return {
status: v.s32(0),
error_code: v.s32(0),
response: {
subscription: {
name: v.str('eac_sdvx_basic'),
},
}
};
}
}
+26 -2
View File
@@ -1,9 +1,9 @@
import { Logger } from "@cordisjs/logger";
import { MongoClient } from "mongodb";
import config from "./utils/config.js";
import { PlayerPlayData } from "./types/index.js";
import { PlayerPlayData } from "./types/p2d/index.js";
import { fromKBinXml } from "./utils/kbinxml.js";
import { Pdata } from "./types/pdata.js";
import { Pdata } from "./types/p2d/pdata.js";
interface DatabaseMeta {
version: number;
@@ -43,6 +43,30 @@ export async function initMongoDb() {
meta.version = 1;
}
if (meta.version < 2) {
const collections = await db.listCollections().toArray();
const tryRenameCollection = async (src: string, dest: string) => {
if (!collections.some(v => v.name === src)) {
return;
}
const col = db.collection(src);
await col.rename(dest);
};
await Promise.all([
tryRenameCollection('player_play_data', 'p2d_play_data'),
tryRenameCollection('player_music_data', 'p2d_music_data'),
tryRenameCollection('player_play_log', 'p2d_play_log'),
tryRenameCollection('player_course_log', 'p2d_course_log'),
tryRenameCollection('player_customize_setting', 'p2d_customize_setting'),
tryRenameCollection('player_rival_data', 'p2d_rival_data'),
])
logger.info('upgraded database to ver 2, renamed prefix player to p2d.');
meta.version = 2;
}
await metaCol.updateOne({}, { $set: meta }, { upsert: true });
return db;
}
+7712
View File
File diff suppressed because it is too large Load Diff
+10587
View File
File diff suppressed because it is too large Load Diff
+17 -12
View File
@@ -1,19 +1,24 @@
import { LZ77 } from '../utils/lz77.js';
import { toKBinXml } from '../utils/kbinxml.js';
export function eacnet(topName: string, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): MethodDecorator {
return function (_target, _propertyKey, descriptor) {
const orig = descriptor.value as Function;
descriptor.value = async function (...args: any[]) {
const obj = await orig.apply(this, args);
const kxml = toKBinXml(topName, obj, encoding);
const compressed = LZ77.compress(kxml.data);
const result = Buffer.allocUnsafe(compressed.length + 0x2E);
export function eacnet(protocol_name: string, topName: string, dataOffset: number) {
return (encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8') => {
return function (_target, _propertyKey, descriptor) {
const orig = descriptor.value as Function;
descriptor.value = async function (...args: any[]) {
const obj = await orig.apply(this, args);
const kxml = toKBinXml(topName, obj, encoding);
const compressed = LZ77.compress(kxml.data);
const result = Buffer.allocUnsafe(compressed.length + dataOffset);
result.write('P2D:2015091800', 0, 'ascii');
result.set(compressed, 0x2E);
result.write(protocol_name, 0, 'ascii');
result.set(compressed, dataOffset);
return result;
} as unknown as any;
return result;
} as unknown as any;
}
}
}
export const p2d = eacnet('P2D:2015091800', 'p2d', 0x2E);
export const sdvx = eacnet('2020090800', 'eacnet', 0x2A);
+4
View File
@@ -114,6 +114,10 @@ async function main(): Promise<void> {
JSON.stringify(ctx.body, (key, value) => {
if (key === 'pdata') {
return '<PDATA>';
} else if (key == 'item') {
return '<ITEM>';
} else if (key == 'param') {
return '<PARAM>';
}
return value;
+22 -6
View File
@@ -6,16 +6,16 @@ import { fromKBinXml } from '../utils/kbinxml.js';
export async function eacnet(ctx: Context, next: Next): Promise<any> {
const body = ctx.request.body as {
request: string;
p2d_token: string;
p2d_token?: string;
} | undefined;
if (!body || !body.request) {
return next();
}
const { request, p2d_token: token } = body;
const { request } = body;
if (!request || !token) {
if (!request) {
return next();
}
@@ -29,12 +29,28 @@ export async function eacnet(ctx: Context, next: Next): Promise<any> {
const decoded = LZ77.decompress(buffer);
const result = fromKBinXml(decoded);
const game = Object.keys(result)[0];
if (result['eacnet']) {
const info = result['eacnet'].info;
ctx.token = info.token;
if (!(game in result)) {
const request = result['eacnet'].request;
if (!request) {
return next();
}
ctx.service = {
name: info.game_id,
method: request.method,
};
ctx.body = request.data ?? {};
ctx.eacnetRequest = request;
return next();
}
// for p2d
const game = Object.keys(result)[0];
if (result[game].params) {
ctx.body = result.p2d.params;
} else {
@@ -46,6 +62,6 @@ export async function eacnet(ctx: Context, next: Next): Promise<any> {
method: result[game].method,
}
ctx.token = token;
ctx.token = body.p2d_token;
return next();
}
+8 -11
View File
@@ -1,41 +1,38 @@
import { Binary, Db } from "mongodb";
import { inject, injectable } from "tsyringe";
import { PlayerPlayData, PlayerMusicData, PlayerPlayLog, PlayerCourseLog, PlayerCustomizeSetting, PlayerRivalData } from "../../types/index.js";
import { Pdata } from "../../types/pdata.js";
import { PlayerPlayData, PlayerMusicData, PlayerPlayLog, PlayerCourseLog, PlayerCustomizeSetting, PlayerRivalData } from "../../types/p2d/index.js";
import { Pdata } from "../../types/p2d/pdata.js";
import { fromKBinXml } from "../../utils/kbinxml.js";
@injectable()
export class UserService {
/**
*
*/
constructor(
@inject(Db)
private readonly db: Db,
) { }
get playDataCol() {
return this.db.collection<PlayerPlayData>('player_play_data');
return this.db.collection<PlayerPlayData>('p2d_play_data');
}
get musicDataCol() {
return this.db.collection<PlayerMusicData>('player_music_data');
return this.db.collection<PlayerMusicData>('p2d_music_data');
}
get playLogCol() {
return this.db.collection<PlayerPlayLog>('player_play_log');
return this.db.collection<PlayerPlayLog>('p2d_play_log');
}
get courseLogCol() {
return this.db.collection<PlayerCourseLog>('player_course_log');
return this.db.collection<PlayerCourseLog>('p2d_course_log');
}
get customizeSettingCol() {
return this.db.collection<PlayerCustomizeSetting>('player_customize_setting');
return this.db.collection<PlayerCustomizeSetting>('p2d_customize_setting');
}
get rivalDataCol() {
return this.db.collection<PlayerRivalData>('player_rival_data');
return this.db.collection<PlayerRivalData>('p2d_rival_data');
}
addCourseLog(coruseLog: PlayerCourseLog) {
+136
View File
@@ -0,0 +1,136 @@
import { Db, FindOptions } from "mongodb";
import { inject, injectable } from "tsyringe";
import { PlayerMusicData, PlayerPlayData, PlayerPlayLog } from "../../types/sdvx/index.js";
import { tokenToCode, tokenToSdvxId } from "../../utils/laochan-id.js";
import { v } from "../../utils/kxml-value.js";
import { dateToString } from "../../utils/time.js";
import { SaveData } from "../../types/sdvx/savedata.js";
@injectable()
export class UserService {
constructor(
@inject(Db)
private readonly db: Db,
) { }
get playDataCol() {
return this.db.collection<PlayerPlayData>('sdvx_player_data');
}
get musicDataCol() {
return this.db.collection<PlayerMusicData>('sdvx_music_data');
}
get playLogCol() {
return this.db.collection<PlayerPlayLog>('sdvx_play_log');
}
getPlayerMusicData(token: string, music_id: number, music_type: number) {
return this.musicDataCol.findOne({ player: token, music_id, music_type });
}
getPlayerMusicDatas(token: string) {
return this.musicDataCol.find({ player: token }).toArray();
}
async upsertMusicData(musicData: PlayerMusicData) {
return await this.musicDataCol.updateOne({
player: musicData.player,
music_id: musicData.music_id,
music_type: musicData.music_type,
}, {
$set: musicData
}, {
upsert: true
});
};
async insertPlayLog(playlog: PlayerPlayLog) {
return this.playLogCol.insertOne(playlog);
}
async hasPlayerData(token: string) {
return !!await this.playDataCol.countDocuments({ _id: token });
}
async savePlayerData(token: string, data: SaveData, name?: string): Promise<boolean> {
const $set = {
save_data: data,
};
if (name) {
$set['name'] = name;
}
const result = await this.playDataCol.updateOne({ _id: token }, { $set }, { upsert: true })
return !!(result.modifiedCount ? result.modifiedCount : result.upsertedCount);
}
async getPlayerData(token: string, options?: FindOptions): Promise<SaveData | undefined> {
const result = await this.playDataCol.findOne({ _id: token }, options);
if (!result)
return undefined;
return result.save_data;
}
async createEmptyPlayerData(token: string, name: string) {
const save_data = {
code: v.str(tokenToCode(token)),
name: v.str(name),
sdvx_id: v.str(tokenToSdvxId(token)),
creator_id: v.u32(0),
gamecoin_block: v.u32(0),
gamecoin_packet: v.u32(0),
blaster_energy: v.u32(0),
blaster_count: v.u32(0),
appeal_id: v.u16(0),
skill_level: v.s16(0),
skill_base_id: v.s16(0),
skill_name_id: v.s16(0),
hispeed: v.s32(0),
lanespeed: v.u32(0),
gauge_option: v.u8(0),
ars_option: v.u8(0),
notes_option: v.u8(0),
early_late_disp: v.u8(0),
draw_adjust: v.s32(0),
eff_c_left: v.u8(0),
eff_c_right: v.u8(1),
last_music_id: v.s32(0),
last_music_type: v.u8(0),
sort_type: v.u8(0),
narrow_down: v.u8(0),
headphone: v.u8(0),
play_count: v.u32(0),
day_count: v.u32(0),
today_count: v.u32(0),
play_chain: v.u32(0),
max_play_chain: v.u32(0),
week_count: v.u32(0),
week_play_count: v.u32(0),
week_chain: v.u32(0),
max_week_chain: v.u32(0),
last_date: v.str(dateToString(new Date())),
start_date: v.str(dateToString(new Date())),
item: {},
item_cloud: {},
item_infinite: {},
skill: {},
param: {},
present: {},
cloud: {
relation: v.s8(1),
},
ea_shop: {
shop_item: {},
},
} as SaveData;
return this.savePlayerData(token, save_data, name);
}
}
+9
View File
@@ -0,0 +1,9 @@
import { readFileSync, writeFileSync } from "fs";
import { toObject, parser } from "../utils/kbinxml.js";
const xml = readFileSync('input.xml', 'utf-8');
const parsedXml = parser.parse(xml);
const obj = toObject(parsedXml);
writeFileSync('output.json', JSON.stringify(obj, undefined, 2));
+7
View File
@@ -10,6 +10,13 @@ export interface ILaochanContext {
method: string,
};
eacnetRequest?: {
service: string;
module: string;
method: string;
data: unknown;
};
logger: Logger;
body?: unknown;
resolve: <T>(resolveToken: InjectionToken<T>) => T;
+70
View File
@@ -0,0 +1,70 @@
import { ObjectId } from "mongodb";
import { SaveData } from "./savedata.js";
export interface PlayerPlayData {
_id: string;
name: string;
save_data: SaveData;
}
export interface PlayerMusicData {
_id: ObjectId;
player: string;
music_id: number;
music_type: number;
score: number;
exscore: number;
clear_type: number;
score_grade: number;
max_chain: number;
play_count: number;
btn_rate: number;
long_rate: number;
vol_rate: number;
best_play_time: Date;
}
export interface PlayerPlayLog {
_id: ObjectId;
player: string;
play_time: Date;
play_id: number;
track_no: number;
music_id: number;
music_type: number;
score: number;
exscore: number;
clear_type: number;
score_grade: number;
max_chain: number;
just: number;
critical: number;
near: number;
error: number;
effective_rate: number;
btn_rate: number;
long_rate: number;
vol_rate: number;
mode: number;
gauge_type: number;
notes_option: number;
online_num: number;
local_num: number;
challenge_type: number;
retry_cnt: number;
pad_type: number;
judge: number[];
drop_frame: number;
drop_frame_max: number;
drop_count: number;
etc: string;
mix_id: number;
mix_like: boolean;
matching: {
score: number;
}[];
is_blaster: boolean;
}
+92
View File
@@ -0,0 +1,92 @@
import { KBoolean, KS16, KS32, KS8, KString, KU16, KU32, KU64, KU8 } from "../../utils/kxml-value.js";
export interface SaveData {
code: KString;
name: KString;
sdvx_id: KString;
creator_id: KU32;
gamecoin_packet: KU32;
gamecoin_block: KU32;
blaster_energy: KU32;
blaster_count: KU32;
appeal_id: KU16;
skill_level: KS16;
skill_base_id: KS16;
skill_name_id: KS16;
hispeed: KS32;
lanespeed: KU32;
gauge_option: KU8;
ars_option: KU8;
notes_option: KU8;
early_late_disp: KU8;
draw_adjust: KS32;
eff_c_left: KU8;
eff_c_right: KU8;
last_music_id: KS32;
last_music_type: KU8;
sort_type: KU8;
narrow_down: KU8;
headphone: KU8;
play_count: KU32;
day_count: KU32;
today_count: KU32;
play_chain: KU32;
max_play_chain: KU32;
week_count: KU32;
week_play_count: KU32;
week_chain: KU32;
max_week_chain: KU32;
last_date: KString;
start_date: KString;
item: { info?: Item[] };
item_cloud: { info?: Item[] };
item_infinite: { info?: Item[] };
skill: { course?: Course[] };
param: { info?: Param[] };
present: { info?: Item[] };
cloud: { relation: KS8 };
ea_shop: EaShop;
}
export interface Course {
ssnid: KS16;
crsid: KS16;
st: KS16;
sc: KS32;
ex: KS32;
ct: KS16;
gr: KS16;
ar: KS16;
cnt: KS16;
}
export interface EaShop {
blaster_pass_enable: KBoolean;
blaster_pass_limit_date: KU64;
shop_item: {
item?: Item[];
}
}
export interface Item {
type: KU8;
id: KU32;
param: KU32;
}
export interface Param {
type: KS32;
id: KS32;
param: KS32;
}
+21
View File
@@ -0,0 +1,21 @@
export function Combine(...modules: ({ new(): any })[]) {
class Modules {
constructor() {
modules.forEach(v => Object.assign(this, new v()));
}
}
modules.forEach(v => {
const descriptors = Object.getOwnPropertyDescriptors(v.prototype);
for (const name in descriptors) {
if (name === 'constructor')
continue;
const descriptor = descriptors[name];
Object.defineProperty(Modules.prototype, name, descriptor);
}
});
return Modules;
}
+14 -4
View File
@@ -1,8 +1,10 @@
import { to_bin, to_xml } from "@kamyu/kbinxml";
import { XMLParser } from "fast-xml-parser";
import _ from "lodash";
import { Serializable } from "./kxml-value.js";
import { writeFileSync } from "fs";
const parser = new XMLParser({
export const parser = new XMLParser({
ignoreAttributes: false,
parseAttributeValue: false,
attributeNamePrefix: '$',
@@ -67,7 +69,7 @@ function parseValue(node: { $__type: string; $__count?: unknown }): any {
};
}
function toObject(node: Record<string, unknown> | unknown[] | string): any {
export function toObject(node: Record<string, unknown> | unknown[] | string): any {
if (node instanceof Array) {
return node.map((v) => toObject(v as Record<string, unknown>));
}
@@ -152,7 +154,7 @@ function serializeValue(value: any, type: string): string {
throw new Error(`unsupported type ${type}`);
}
function serializeObject(obj: Object, name: string, linePrefix: string = '') {
function serializeObject(obj: Serializable, name: string, linePrefix: string = '') {
let output = `${linePrefix}<${name}`;
const entries = Object.entries(obj);
@@ -175,12 +177,19 @@ function serializeObject(obj: Object, name: string, linePrefix: string = '') {
attrs.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
for (const attr of attrs) {
if (attr[1] === undefined)
continue;
output += ` ${attr[0]}="${_.escape(attr[1])}"`;
}
output += '>';
if (value) {
if (value[1] === undefined) {
return '';
}
const type = attrs.find(v => v[0] == '__type');
if (!type) {
@@ -217,7 +226,8 @@ function serializeObject(obj: Object, name: string, linePrefix: string = '') {
return output + `</${name}>\n`;
}
export function toKBinXml(topName: string, obj: unknown, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8') {
export function toKBinXml(topName: string, obj: Serializable, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8') {
const xml = `<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
writeFileSync('dump.xml', xml);
return to_bin(xml);
}
+55 -21
View File
@@ -14,8 +14,42 @@ export type ValueTypes = 's8' |
'str' | 'string' |
'time';
function vg(type: ValueTypes) {
return (value: unknown, attrs?: object) => {
type AttributeProperty<T> = {
[K in keyof T as K extends string ? `$${K}` : never]: T[K]
}
export type KValueG<T extends ValueTypes, VT = unknown> = {
$__type: T;
__value: VT | VT[];
} & AttributeProperty<{
[key: string]: string | number;
}>;
export type KS8 = KValueG<'s8', number>;
export type KU8 = KValueG<'u8', number>;
export type KS16 = KValueG<'s16', number>;
export type KU16 = KValueG<'u16', number>;
export type KS32 = KValueG<'s32', number>;
export type KU32 = KValueG<'u32', number>;
export type KS64 = KValueG<'s64', number>;
export type KU64 = KValueG<'u64', number>;
export type KFloat = KValueG<'float', number>;
export type KDouble = KValueG<'double', number>;
export type KBoolean = KValueG<'bool', boolean> | KValueG<'b', boolean>;
export type KBinary = KValueG<'bin', Buffer> | KValueG<'binary', Buffer>;
export type KString = KValueG<'str', string> | KValueG<'string', string>;
export type KIPv4 = KValueG<'ip4', string>;
export type KTime = KValueG<'time', Date | number>;
export type KValue = KS8 | KU8 | KS16 | KU16 | KS32 | KS64 | KU64 | KFloat | KDouble | KBoolean | KBinary | KString | KIPv4 | KTime;
export type Serializable = {
[key: string]: KValue | Serializable | Serializable[]
} | AttributeProperty<{
[key: string]: string | number;
}>;
function vg<T extends ValueTypes, VT>(type: T) {
return (value: VT | VT[], attrs?: object): KValueG<T, VT> => {
const result = {
$__type: type,
__value: value,
@@ -31,23 +65,23 @@ function vg(type: ValueTypes) {
};
}
export const v: Record<ValueTypes, (value: unknown, attrs?: object) => object> = {
s8: vg('s8'),
u8: vg('u8'),
s16: vg('s16'),
u16: vg('u16'),
s32: vg('s32'),
u32: vg('u32'),
s64: vg('s64'),
u64: vg('u64'),
float: vg('float'),
double: vg('double'),
b: vg('b'),
bool: vg('bool'),
bin: vg('bin'),
binary: vg('binary'),
ip4: vg('ip4'),
str: vg('str'),
string: vg('string'),
time: vg('time'),
export const v = {
s8: vg<'s8', number>('s8'),
u8: vg<'u8', number>('u8'),
s16: vg<'s16', number>('s16'),
u16: vg<'u16', number>('u16'),
s32: vg<'s32', number>('s32'),
u32: vg<'u32', number>('u32'),
s64: vg<'s64', number>('s64'),
u64: vg<'u64', number>('u64'),
float: vg<'float', number>('float'),
double: vg<'double', number>('double'),
b: vg<'b', boolean>('b'),
bool: vg<'bool', boolean>('bool'),
bin: vg<'bin', Buffer>('bin'),
binary: vg<'binary', Buffer>('binary'),
ip4: vg<'ip4', string>('ip4'),
str: vg<'str', string>('str'),
string: vg<'string', string>('string'),
time: vg<'time', Date | number>('time'),
}
+29 -1
View File
@@ -1,9 +1,37 @@
import { createHash } from 'crypto';
import highwayHash from 'highwayhash';
export function fromToken(token: string) {
export function tokenToInfinitasId(token: string) {
const key = createHash('md5')
.update(token, 'utf-8')
.digest('hex');
return ('LCHAN' + key).slice(0, 13);
}
const HIGHWAY_KEY = Buffer.from('LAOCHANID_HIGHWAY_KEY_NOTSECURE!');
export function tokenToHash(token: string) {
const key = highwayHash.asHexString(HIGHWAY_KEY, Buffer.from(token));
return key.toUpperCase();
}
export function tokenToCardNumber(token: string) {
const hash = tokenToHash(token);
return ('LCHAN' + hash.toUpperCase()).slice(0, 16);
}
export function tokenToCode(token: string) {
const key = highwayHash.asUInt32Low(HIGHWAY_KEY, Buffer.from(token));
return 'MOAI' + key.toString().padStart(9, '0').slice(0, 9);
}
export function tokenToSnsId(token: string) {
const key = highwayHash.asUInt32Low(HIGHWAY_KEY, Buffer.from(token));
return key.toString().padStart(8, '0');
}
export function tokenToSdvxId(token: string) {
const snsId = tokenToSnsId(token);
return 'SV-' + snsId.slice(0, 4) + '-' + snsId.slice(4, 8);
}
+7
View File
@@ -0,0 +1,7 @@
export function dateToString(date: Date) {
const isoStr = date.toISOString();
const d = isoStr.split('T')[0]
const t = isoStr.split('T')[1].split('.')[0];
return `${d} ${t}`;
}