diff --git a/.eslintrc.json b/.eslintrc.json index 7545777..25d7ee6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -24,6 +24,7 @@ // https://typescript-eslint.io/rules/explicit-function-return-type/ "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-explicit-any": "off" + "@typescript-eslint/no-explicit-any": "off", + "no-constant-condition": "off" } } diff --git a/package-lock.json b/package-lock.json index f582f10..230fc45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "dotenv": "^16.4.5", "fast-xml-parser": "^4.3.2", "koa": "^2.15.0", + "koa-bodyparser": "^4.4.1", "lodash": "^4.17.21", "mongodb": "^6.4.0", "pug": "^3.0.2", @@ -26,6 +27,7 @@ "devDependencies": { "@types/jest": "~29.5", "@types/koa": "^2.13.12", + "@types/koa-bodyparser": "^4.3.12", "@types/lodash": "^4.14.202", "@types/node": "~20", "@types/pug": "^2.0.10", @@ -2045,6 +2047,15 @@ "@types/node": "*" } }, + "node_modules/@types/koa-bodyparser": { + "version": "4.3.12", + "resolved": "https://registry.npmmirror.com/@types/koa-bodyparser/-/koa-bodyparser-4.3.12.tgz", + "integrity": "sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==", + "dev": true, + "dependencies": { + "@types/koa": "*" + } + }, "node_modules/@types/koa-compose": { "version": "3.2.8", "resolved": "https://registry.npmmirror.com/@types/koa-compose/-/koa-compose-3.2.8.tgz", @@ -2736,6 +2747,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cache-content-type": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/cache-content-type/-/cache-content-type-1.0.1.tgz", @@ -2749,13 +2768,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/callsites": { @@ -2873,6 +2897,17 @@ "node": ">= 0.12.0" } }, + "node_modules/co-body": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/co-body/-/co-body-6.1.0.tgz", + "integrity": "sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==", + "dependencies": { + "inflation": "^2.0.0", + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -2949,6 +2984,11 @@ "node": ">= 0.8" } }, + "node_modules/copy-to": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==" + }, "node_modules/cordis": { "version": "3.5.1", "resolved": "https://registry.npmmirror.com/cordis/-/cordis-3.5.1.tgz", @@ -3062,13 +3102,13 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -3214,6 +3254,25 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.19.11", "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.19.11.tgz", @@ -3873,14 +3932,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/get-package-type": { @@ -4009,17 +4072,17 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" } @@ -4104,6 +4167,17 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -4157,6 +4231,14 @@ "node": ">=0.8.19" } }, + "node_modules/inflation": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/inflation/-/inflation-2.1.0.tgz", + "integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5093,6 +5175,19 @@ "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" } }, + "node_modules/koa-bodyparser": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/koa-bodyparser/-/koa-bodyparser-4.4.1.tgz", + "integrity": "sha512-kBH3IYPMb+iAXnrxIhXnW+gXV8OTzCu8VPDqvcDHW9SQrbkHmqPQtiZwrltNmSq6/lpipHnT7k7PsjlVD7kK0w==", + "dependencies": { + "co-body": "^6.0.0", + "copy-to": "^2.0.1", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/koa-compose": { "version": "4.1.0", "resolved": "https://registry.npmmirror.com/koa-compose/-/koa-compose-4.1.0.tgz", @@ -5916,6 +6011,17 @@ } ] }, + "node_modules/qs": { + "version": "6.12.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5936,6 +6042,43 @@ } ] }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -6140,6 +6283,11 @@ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -6174,14 +6322,16 @@ "dev": true }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6213,6 +6363,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6724,6 +6888,14 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 281aac5..d10d8d6 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "devDependencies": { "@types/jest": "~29.5", "@types/koa": "^2.13.12", + "@types/koa-bodyparser": "^4.3.12", "@types/lodash": "^4.14.202", "@types/node": "~20", "@types/pug": "^2.0.10", @@ -49,6 +50,7 @@ "dotenv": "^16.4.5", "fast-xml-parser": "^4.3.2", "koa": "^2.15.0", + "koa-bodyparser": "^4.4.1", "lodash": "^4.17.21", "mongodb": "^6.4.0", "pug": "^3.0.2", diff --git a/src/decorators/to-kxml.ts b/src/decorators/eacnet.ts similarity index 82% rename from src/decorators/to-kxml.ts rename to src/decorators/eacnet.ts index 06d2668..0cbd73c 100644 --- a/src/decorators/to-kxml.ts +++ b/src/decorators/eacnet.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import { to_bin } from '@kamyu/kbinxml'; +import { LZ77 } from '../utils/lz77.js'; function serializeValue(value: any, type: string): string { if ( @@ -115,7 +116,7 @@ function serializeObject(obj: Object, name: string, linePrefix: string = '') { return output + `\n`; } -export function kxml(topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): MethodDecorator { +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[]) { @@ -123,27 +124,17 @@ export function kxml(topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JI const xml = `` + serializeObject(obj, topName); const kxml = to_bin(xml); + const compressed = LZ77.compress(kxml.data); + const result = Buffer.allocUnsafe(compressed.length + 0x2E); - return Buffer.from(kxml.data); + result.write('P2D:2015091800', 0, 'ascii'); + result.set(compressed, 0x2E); + + return result; } as unknown as any; } } -export function serviceKxml(service: string, topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): (method: string) => MethodDecorator { - return function (method: string) { - const toKxml = kxml(topName, encoding); - return function (_target, _propertyKey, descriptor) { - const orig = descriptor.value as Function; - descriptor.value = async function (...args: any[]) { - const obj = await orig.apply(this, args); - return { [service]: { method, ...obj } }; - } as unknown as any; - - toKxml(_target, _propertyKey, descriptor); - } - } -} - // console.log(serializeObject({ // $attr: 'attr1', // sb: { diff --git a/src/decorators/render-kxml.ts b/src/decorators/render-kxml.ts deleted file mode 100644 index c35b870..0000000 --- a/src/decorators/render-kxml.ts +++ /dev/null @@ -1,21 +0,0 @@ -import pug from 'pug'; -import { to_bin } from '@kamyu/kbinxml'; - -export function kxmlTemplate( - templatePath: string, - encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8', -): MethodDecorator { - const template = pug.compileFile('templates/' + templatePath + '.pug'); - - 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 xml = - `` + template(obj); - const kxml = to_bin(xml); - - return Buffer.from(kxml.data); - } as unknown as any; - }; -} diff --git a/src/main.ts b/src/main.ts index a918470..c506320 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,13 @@ import 'reflect-metadata'; import Koa, { DefaultState } from 'koa'; +import bodyParser from 'koa-bodyparser'; import { InjectionToken, Provider, container } from 'tsyringe'; import { ILaochanContext } from './types.js'; import { DefaultService } from './services/default.js'; import { Logger } from '@cordisjs/logger'; -import { crypto } from './middlewares/crypto.js'; -import { binaryBody } from './middlewares/binarybody.js'; -import { parseKxml } from './middlewares/parseKxml.js'; -import { decompress } from './middlewares/decompress.js'; +import { eacnet } from './middlewares/eacnet.js'; import config from './utils/config.js'; import { MongoClient, Db } from 'mongodb'; @@ -30,7 +28,7 @@ function register( } async function main(): Promise { - const logger = new Logger('laochan-net'); + const logger = new Logger('laochan-eacnet'); logger.info('initialization...'); @@ -59,38 +57,22 @@ async function main(): Promise { ctx.body = `${e.stack ?? 'Internal Error'}`; } }) - .use(binaryBody) - .use(crypto) - .use(decompress) - .use(parseKxml) + .use(bodyParser({ + formLimit: '1mb', + })) + .use(eacnet) .use(async (ctx, next) => { - if (typeof ctx.query.f !== 'string') { + if (!ctx.service) { throw new Error('invaild service'); } - if (typeof ctx.query.model !== 'string') { - throw new Error('invaild model'); - } - - // eslint-disable-next-line prefer-const - let [serviceName, method] = ctx.query.f.split('.'); - - ctx.model = ctx.query.model; - ctx.service = { - name: serviceName, - method, - }; - - ctx.body = ctx.body[ctx.service.name]; ctx.logger = ctx.service - ? register(`logger:${ctx.service.name}`, { - useValue: new Logger(ctx.service.name), + ? register(`logger:${ctx.service.name}:${ctx.service.method}`, { + useValue: new Logger(`${ctx.service.name}:${ctx.service.method}`), }) : logger; - let service: object | undefined = ctx.service - ? tryResolve(ctx.service.name) - : container.resolve(DefaultService); + let service: object | undefined = tryResolve(ctx.service.name); if (service === undefined) { try { @@ -100,43 +82,53 @@ async function main(): Promise { module = await import( `./services/${ctx.service.name}/index.js` ); - } catch { - module = await import( - `./services/${ctx.service.name}.js` - ); + } catch (e) { + if (e.code != 'ERR_MODULE_NOT_FOUND') { + logger.error( + `load service error %s`, + JSON.stringify(e, Object.getOwnPropertyNames(e), 2), + ); + } else { + module = await import( + `./services/${ctx.service.name}.js` + ); + } } service = register(ctx.service.name, { useClass: module.default, - }); } catch (e) { - logger.error( - `load service error %s`, - JSON.stringify(e, Object.getOwnPropertyNames(e), 2), - ); + if (e.code != 'ERR_MODULE_NOT_FOUND') { + logger.error( + `load service error %s`, + JSON.stringify(e, Object.getOwnPropertyNames(e), 2), + ); + } } } logger.info( - '%s.$s [%s]: request = %s', + '%s.%s [%s]: request = %s', ctx.service.name, ctx.service.method, - ctx.pcbid, - JSON.stringify(ctx.request.body), + ctx.token, + JSON.stringify(ctx.body), ); - if (service === undefined || !(method in service)) { - logger.warn(`unimplemented method ${ctx.service.method} in ${ctx.service.name}`); + let method = ctx.service.method; + + if (!service || !(service[method])?.bind) { + logger.warn(`unimplemented method ${method} in ${ctx.service.name}`); service = container.resolve(DefaultService); method = 'default'; } - ctx.body = await service[method](ctx); + ctx.body = await (service[method] as Function).bind(service)(ctx); ctx.status = 200; - logger.info('%s [server]: status = %d', ctx.service, ctx.status); + logger.info('%s.%s [server]: status = %d', ctx.service.name, ctx.service.method, ctx.status); return await next(); }); diff --git a/src/middlewares/binarybody.ts b/src/middlewares/binarybody.ts deleted file mode 100644 index ab3ccc1..0000000 --- a/src/middlewares/binarybody.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Next } from 'koa'; -import { Context } from '../types.js'; - -export async function binaryBody(ctx: Context, next: Next): Promise { - if (typeof ctx.headers['content-length'] !== 'string') { - return next(); - } - - const length = parseInt(ctx.headers['content-length']); - if (length === 0) { - return next(); - } - - ctx.request.body = await new Promise((resolve, reject) => { - const buffer = Buffer.allocUnsafe(length); - let len = 0; - - ctx.req.on('data', (chunk: Buffer) => { - chunk.copy(buffer, len); - len += chunk.length; - - if (len == length) { - resolve(buffer); - } - }); - - ctx.req.on('close', () => { - if (len < length) { - reject(new Error('socket hanged up unexceptly.')); - } - }); - }); - - return next(); -} diff --git a/src/middlewares/crypto.ts b/src/middlewares/crypto.ts deleted file mode 100644 index b2e0650..0000000 --- a/src/middlewares/crypto.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Next } from 'koa'; -import { Context } from '../types.js'; -import { rc4Transfrom } from '../utils/rc4.js'; - -export async function crypto(ctx: Context, next: Next): Promise { - if (typeof ctx.req.headers['x-eamuse-info'] !== 'string') { - return next(); - } - - if (!(ctx.request.body instanceof Buffer)) { - return next(); - } - - { - const eamuseInfo: string[] = ctx.req.headers['x-eamuse-info'].split('-'); - const version = eamuseInfo[0]; - const time = eamuseInfo[1]; - const salt = eamuseInfo[2]; - - if (version !== '1') { - throw new Error(`unsupported crypto version ${version}`); - } - - const decrypted = rc4Transfrom(time + salt, ctx.request.body); - ctx.request.body = decrypted; - } - - await next(); - - if (!(ctx.body instanceof Buffer)) { - return; - } - - const time = Math.floor(new Date().valueOf() / 1000) - .toString(16) - .padStart(8, '0'); - const salt = Math.floor(Math.random() * 0xffff) - .toString(16) - .padStart(4, '0'); - - const encrypted = rc4Transfrom(time + salt, ctx.body); - ctx.body = encrypted; - - ctx.set('X-Eamuse-Info', `1-${time}-${salt}`); -} diff --git a/src/middlewares/decompress.ts b/src/middlewares/decompress.ts deleted file mode 100644 index b6b4097..0000000 --- a/src/middlewares/decompress.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Next } from 'koa'; -import { Context } from '../types.js'; -import { lz77Decode } from '../utils/lz77.js'; - -export async function decompress(ctx: Context, next: Next): Promise { - if (!(ctx.request.body instanceof Buffer)) { - return next(); - } - - if (typeof ctx.request.headers['x-compress'] !== 'string') { - return next(); - } - - // no compression for response - ctx.set('X-Compress', 'none'); - - if (ctx.request.headers['x-compress'] == 'none') { - return next(); - } - - if (ctx.request.headers['x-compress'] != 'lz77') { - throw new Error('unsupported compression mehotd'); - } - - ctx.request.body = lz77Decode(ctx.request.body); - return next(); -} diff --git a/src/middlewares/parseKxml.ts b/src/middlewares/eacnet.ts similarity index 68% rename from src/middlewares/parseKxml.ts rename to src/middlewares/eacnet.ts index 4cab0dd..1386e4c 100644 --- a/src/middlewares/parseKxml.ts +++ b/src/middlewares/eacnet.ts @@ -1,5 +1,6 @@ import { Next } from 'koa'; import { Context } from '../types.js'; +import { LZ77 } from '../utils/lz77.js'; import { to_xml } from '@kamyu/kbinxml'; import { XMLParser } from 'fast-xml-parser'; @@ -83,26 +84,43 @@ function kxmlToObject(node: Record): any { return obj; } -export async function parseKxml(ctx: Context, next: Next): Promise { - if (!(ctx.request.body instanceof Buffer)) { +export async function eacnet(ctx: Context, next: Next): Promise { + const body = ctx.request.body as { + request: string; + p2d_token: string; + } | undefined; + + if (!body || !body.request) { return next(); } - if ('x-dev' in ctx.request.headers) { - ctx.request.body = JSON.parse(ctx.request.body.toString('utf-8')); + const { request, p2d_token: token } = body; + const buffer = Buffer.from(request + .replaceAll(' ', '+') + .replaceAll('-', '+') + .replaceAll('_', '/') + '===', + 'base64' + ); + const decoded = LZ77.decompress(buffer); + const xmlResult = kxmlToObject(parser.parse(to_xml(decoded).data)); + + const game = Object.keys(xmlResult)[0]; + + if (!(game in xmlResult)) { + return next(); + } + + if(xmlResult[game].params) { + ctx.body = xmlResult.p2d.params; } else { - const xmlResult = to_xml(ctx.request.body); - ctx.request.body = kxmlToObject(parser.parse(xmlResult.data)); + ctx.body = {}; } - const body = ctx.request.body as any; - - if ('call' in body) { - ctx.pcbid = body.call.srcid; - ctx.tag = body.call.tag; - - ctx.body = body.call; + ctx.service = { + name: game, + method: xmlResult[game].method, } + ctx.token = token; return next(); } diff --git a/src/services/IIDX31music/index.ts b/src/services/IIDX31music/index.ts deleted file mode 100644 index ac804d4..0000000 --- a/src/services/IIDX31music/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { serviceKxml } from "../../decorators/to-kxml.js"; -// import { v } from "../../utils/kxml-value.js"; - -const resp = serviceKxml('IIDX31music'); - -export default class { - @resp('crate') - async crate() { - return { - $expire: 1800, - c: [ - // v.s32('114514', { mid: 11451 }), - ], - }; - } - - @resp('reg') - async reg() { - return { - $clid: 0, - $crate: 0, - $frate: 0, - $mid: 0, - $rankside: 0, - $status: 0, - shopdata: { - $rank: 252, - }, - ranklist: { - $total_user_num: 1, - data: [{ - $clflg: 4, - $dgrade: -1, - $sgrade: -1, - $iidx_id: '114514', - $myFlg: '0', - $name: 'MK_', - $opname: '雷窝', - $pid: 0, - $rnum: 0, - $score: 0, - $update: 0, - $body: 0, - $face: 0, - $hair: 0, - $hand: 0, - $head: 0, - }] - } - } - } -} diff --git a/src/services/IIDX31pc/index.ts b/src/services/IIDX31pc/index.ts deleted file mode 100644 index 7c3b519..0000000 --- a/src/services/IIDX31pc/index.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { serviceKxml } from "../../decorators/to-kxml.js"; -import { Context } from "../../types.js"; -import { v } from "../../utils/kxml-value.js"; - -interface GetPCDataCall { - ccode: string, - cid: string, - ctype: string, - did: string, - lid: string, - pid: string, - rid: string, -} - -const resp = serviceKxml('IIDX31pc'); - -export default class { - @resp('common') - async common() { - return { - system_voice_phase: { - $phase: 0, - }, - ir: { - $beat: 0, - }, - vip_pass_black: {}, - common_evnet: { - $flg: -1, - }, - play_video: {}, - music_retry: {}, - display_asio_logo: {}, - lane_gacha: {}, - tourism_booster: {}, - cm_movie_info: { - $type: 3, - }, - monthly_mranking: v.u16([]), - total_mranking: v.u16([]), - hitchart: [{ - $kind: 0, - $period: 0, - ranking: [{ - $music_id: 31021, - $rank: 1 - }] - }], - $expire: 180, - }; - } - - @resp('get') - get(ctx: Context) { - const { cid } = ctx.body as GetPCDataCall; - return { - pcdata: this.getPlayerPcData(cid), - bind_eaappli: {}, - secret: { - flg1: v.s64([-1, -1, 1048575]), - flg2: v.s64([-1, -1, 0]), - flg3: v.s64([-1, -1, 0]), - flg4: v.s64([-1, -1, 0]), - }, - favorite: { - sp_mlist: v.bin(Buffer.alloc(1, 0)), - sp_clist: v.bin(Buffer.alloc(1, 0)), - dp_mlist: v.bin(Buffer.alloc(1, 0)), - dp_clist: v.bin(Buffer.alloc(1, 0)), - }, - extra_favorite: [{ - $folder_id: 0, - sp_mlist: v.bin(Buffer.alloc(1, 0)), - sp_clist: v.bin(Buffer.alloc(1, 0)), - dp_mlist: v.bin(Buffer.alloc(1, 0)), - dp_clist: v.bin(Buffer.alloc(1, 0)), - }, { - $folder_id: 0, - sp_mlist: v.bin(Buffer.alloc(1, 0)), - sp_clist: v.bin(Buffer.alloc(1, 0)), - dp_mlist: v.bin(Buffer.alloc(1, 0)), - dp_clist: v.bin(Buffer.alloc(1, 0)), - }], - grade: { - $dgid: -1, - $sgid: -1, - g: v.u8([0, 13, 3, 58]), - }, - skin: v.s16(new Array(20).fill(0)), - tdjskin: v.s16([0, 0, 0, 0]), - qprodata: v.s16([0, 0, 0, 0, 0]), - spdp_rival: { - $flg: 1, - }, - rlist: {}, - ex: {}, - secret_course_data: {}, - weekly: { - $mid: 31048, - $wid: 2041, - }, - weekly_score: [], - visitor: { - $anum: 1, - $pnum: 1, - $snum: 1, - $vs_flg: 0, - }, - step: { - - } - } - } - - - private getPlayerPcData(id: string) { - const data = { - d_auto_adjust: 0, - d_auto_scrach: 0, - d_camera_layout: 0, - d_classic_hispeed: 0, - d_disp_judge: 0, - d_gauge_disp: 0, - d_ghost_score: 0, - d_gno: 0, - d_graph_score: 0, - d_gtype: 0, - d_hispeed: 0.000000, - d_judge: 0, - d_judgeAdj: 0, - d_lane_brignt: 0, - d_liflen: 0, - d_notes: 0.000000, - d_opstyle: 0, - d_pace: 0, - d_sdlen: 0, - d_sdtype: 0, - d_sorttype: 0, - d_sub_gno: 0, - d_timing: 0, - d_timing_split: 0, - d_tsujigiri_disp: 0, - d_visualization: 0, - dach: 0, - dp_opt: 1048577, - dp_opt2: 1048577, - dpnum: 0, - gpos: 0, - id: 0, - idstr: '00000000', - mode: 3, - name: 'MK_', - ngrade: 0, - pid: 48, - player_kind: 0, - pmode: 0, - rtype: 0, - s_auto_adjust: 1, - s_auto_scrach: 0, - s_camera_layout: 0, - s_classic_hispeed: 0, - s_disp_judge: 1, - s_gauge_disp: 0, - s_ghost_score: 0, - s_gno: 0, - s_graph_score: 0, - s_gtype: 1, - s_hispeed: 7.888014, - s_judge: 0, - s_judgeAdj: -22, - s_lane_brignt: 0, - s_liflen: 90, - s_notes: 28.497925, - s_opstyle: 2, - s_pace: 0, - s_sdlen: 258, - s_sdtype: 1, - s_sorttype: 0, - s_sub_gno: 0, - s_timing: 1, - s_timing_split: 1, - s_tsujigiri_disp: 0, - s_visualization: 0, - sach: 49, - sp_opt: 17180950528, - spnum: 2, - cardid: id, - }; - - return Object.fromEntries( - Object.entries(data).map(v => ['$' + v[0], v[1]]) - ); - } -} diff --git a/src/services/IIDX31shop/index.ts b/src/services/IIDX31shop/index.ts deleted file mode 100644 index 50f3693..0000000 --- a/src/services/IIDX31shop/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { serviceKxml } from "../../decorators/to-kxml.js"; - -const resp = serviceKxml('IIDX31shop'); - -export default class { - @resp('getname') - async getname() { - return { - $cls_opt: 14400, - $expire: 0, - $opname: '原神, 启动', - $pid: 30, - }; - } - - @resp('sentinfo') - async sentinfo() { - return { - $status: 0, - }; - } -} diff --git a/src/services/IIDX31streaming/index.ts b/src/services/IIDX31streaming/index.ts deleted file mode 100644 index 341a15e..0000000 --- a/src/services/IIDX31streaming/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { serviceKxml } from "../../decorators/to-kxml.js"; - -const resp = serviceKxml('IIDX31streaming'); - -export default class { - @resp('common') - common() { - return { - $expire: Math.floor(new Date().valueOf() / 1000) + 259200, - cm_info: {}, - }; - } -} diff --git a/src/services/cardmng/index.ts b/src/services/cardmng/index.ts deleted file mode 100644 index 4a2040c..0000000 --- a/src/services/cardmng/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { serviceKxml } from "../../decorators/to-kxml.js"; -import { Context } from "../../types.js"; - -interface CardInquire { - cardid: string, - cardtype: string, -} - -interface CardAuth { - pass: string, - refid: string, -} - -interface GetRefId { - cardid: string, - passwd: string; -} - -const resp = serviceKxml('cardmng'); - -export default class { - @resp('authpass') - authpass(ctx: Context) { - const { refid, pass } = ctx.body as CardAuth; - ctx.logger.info('auth %s with pass %s', refid, pass); - - return { - $status: 0, - }; - } - - @resp('getrefid') - getrefid(ctx: Context) { - const { cardid, passwd } = ctx.body as GetRefId; - ctx.logger.info('registering %s with passwd %s.', cardid, passwd); - - return { - $dataid: cardid, - $refid: cardid, - } - } - - @resp('inquire') - inquire(ctx: Context) { - const { cardid, cardtype } = ctx.body as CardInquire; - ctx.logger.info('card %s (type %d) inquire', cardid, cardtype); - - return { - $binded: 1, - $newflag: 0, - $ecflag: 1, - $expired: 0, - $dataid: cardid, - $refid: cardid, - $status: 0, - }; - } -} diff --git a/src/services/default.ts b/src/services/default.ts index 6c208ab..fba5ac8 100644 --- a/src/services/default.ts +++ b/src/services/default.ts @@ -1,14 +1,20 @@ import { Context } from '../types.js'; -import { kxml } from '../decorators/to-kxml.js'; +import { eacnet } from '../decorators/eacnet.js'; +import { v } from '../utils/kxml-value.js'; export class DefaultService { - @kxml() async default(ctx: Context) { - return { - [ctx.service.name]: { - $method: ctx.service.method, - status: '0', - } - } + const descriptor = { + value: () => ({ + status: v.s32(0), + error: v.s32(0), + result: { + dummy: v.u8(0), + } + }) + }; + + eacnet(ctx.service.name)(undefined, undefined, descriptor); + return descriptor.value(); } } diff --git a/src/services/eacoin/index.ts b/src/services/eacoin/index.ts deleted file mode 100644 index fa09bbd..0000000 --- a/src/services/eacoin/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { serviceKxml } from "../../decorators/to-kxml.js"; -import { v } from "../../utils/kxml-value.js"; - -const resp = serviceKxml('eacoin'); - -export default class { - @resp('checkin') - checkin() { - return { - $status: 0, - balance: v.s32(114514), - sessid: v.str('0000000000000000'), - acstatus: v.u8(0), - sequence: v.s16(1), - acid: v.str('0000000000000000'), - acname: v.str('0000000000000000'), - } - } - - @resp('consume') - consume() { - return { - $status: 0, - autocharge: v.u8(0), - acstatus: v.u8(0), - balance: v.u8(114514), - } - } -} diff --git a/src/services/facility/index.ts b/src/services/facility/index.ts deleted file mode 100644 index 8fbfe9a..0000000 --- a/src/services/facility/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { kxmlTemplate } from '../../decorators/render-kxml.js'; -import { singleton } from 'tsyringe'; - -@singleton() -export default class { - @kxmlTemplate('facility/get') - async get(): Promise { - return { - status: 0, - location: { - id: 'EA000000', - type: 0, - country: 'JP', - region: 'JP-0', - name: 'LaoChan Test', - countryName: 'LaoChan', - countryJName: '老禅', - regionName: 'BianYu', - regionJName: '變魚', - customerCode: 'LaoChan', - companyCode: 'LaoChan', - latitude: 0, - longitude: 0, - accuracy: 0, - }, - line: { - id: '0', - class: 8, - upClass: 8, - rtt: 22, - }, - portfw: { - globalIp: '127.0.0.1', - globalPort: 5700, - privatePort: 5700, - }, - public: { - flag: 0, - name: 'LaoChan Test', - latitude: '0', - longitude: '0', - }, - share: { - url: '老禅 LaoChan LaoChan 老禅', - eapass: 365, - eacoin: { - notchAmount: 0, - notchCount: 0, - supplyLimit: 100000, - }, - }, - calendar: { - year: 2024, - holiday: [ - 0, 1, 8, 41, 53, 79, 118, 122, 123, 124, 197, 222, 260, 265, 281, 306, - 326, 365, 372, 406, 407, 418, 444, 484, 488, 489, 490, 491, 561, 588, - 589, 624, 630, 631, 652, 672, 673, 692, - ], - }, - }; - } -} diff --git a/src/services/message/index.ts b/src/services/message/index.ts deleted file mode 100644 index ae84945..0000000 --- a/src/services/message/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { serviceKxml } from '../../decorators/to-kxml.js'; - -const resp = serviceKxml('message'); - -export default class { - @resp('get') - async get(): Promise { - return { - $status: 0, - $expire: 60, - }; - } -} diff --git a/src/services/p2d/index.ts b/src/services/p2d/index.ts new file mode 100644 index 0000000..cdcc8f4 --- /dev/null +++ b/src/services/p2d/index.ts @@ -0,0 +1,21 @@ +import { Service } from "./service.js"; + +function Combine(...modules: ({ new(): any })[]) { + class Modules { + constructor() { + modules.forEach(v => Object.assign(this, new v())); + } + } + + modules.forEach(v => { + Object.getOwnPropertyNames(v.prototype) + .filter(p => p != 'constructor') + .forEach(p => Modules.prototype[p] = v.prototype[p]) + }); + + return Modules; +} + +export default class extends Combine(Service) { + +} diff --git a/src/services/p2d/service.ts b/src/services/p2d/service.ts new file mode 100644 index 0000000..8c4febe --- /dev/null +++ b/src/services/p2d/service.ts @@ -0,0 +1,379 @@ +import { eacnet } 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') + async getServerState() { + return { + status: v.s32(0), + error: v.s32(0), + result: { + server_state: v.s32(1), + mainte_start_clock: v.u64(0), + mainte_end_clock: v.u64(0), + } + } + } + + @eacnet('p2d') + async getServerClock() { + return { + status: v.s32(0), + error: v.s32(0), + result: { + server_clock: v.u64(new Date().valueOf()), + } + } + } + + @eacnet('p2d') + async getServerValues() { + return { + status: v.s32(0), + error: v.s32(0), + result: { + bit_boost_magnification: v.str('2'), + debug_user_force_subscription: v.str('TRUE'), + devline_ignore_maintenance: v.str('TRUE'), + enable_file_upload: v.str('TRUE'), + frame_bit_magnification: v.str('TRUE'), + open_bemani_linkage_hinabita2023: v.str('TRUE'), + open_bemani_linkage_knst2024sp: v.str('TRUE'), + open_bemani_linkage_mixup: v.str('TRUE'), + } + } + } + + @eacnet('p2d') + async checkGameVersion() { + return { + status: v.s32(0), + error: v.s32(0), + result: { + permission: v.s32(1), + } + }; + } + + @eacnet('p2d') + async heartbeat(ctx: Context) { + return { + status: v.s32(0), + error: v.s32(0), + result: { + state: v.s32(1), + next_clock: v.u64(new Date().valueOf() + 36e5), + token: ctx.token, + time_remain: v.s32(0x7fffffff), + subscription_status: v.s32(1), + } + }; + } + + @eacnet('p2d') + async checkSendLogAvailable() { + return { + status: v.s32(0), + error: v.s32(0), + result: { + status: v.s32(1), + daily_count: v.s32(999), + } + }; + } + + @eacnet('p2d') + async getServices() { + return { + status: v.s32(0), + error: v.s32(0), + result: { + service_num: v.s32(63), + service: [ + { + service_name: v.str(`addPoint`), + url: v.str(`${config.selfUrl}/needAuth/AddPoint`), + }, + { + service_name: v.str(`addPointDirect`), + url: v.str(`${config.selfUrl}/needAuth/AddPointDirect`), + }, + { + service_name: v.str(`cancelReserveChangePoint`), + url: v.str( + `${config.selfUrl}/needAuth/CancelReserveChangePoint`, + ), + }, + { + service_name: v.str(`cancelReserveConsumeItem`), + url: v.str( + `${config.selfUrl}/needAuth/CancelReserveConsumeItem`, + ), + }, + { + service_name: v.str(`checkChampionshipInfo`), + url: v.str( + `${config.selfUrl}/needlessAuth/CheckChampionshipInfo`, + ), + }, + { + service_name: v.str(`checkGameStart`), + url: v.str(`${config.selfUrl}/preProcess/CheckGameStart`), + }, + { + service_name: v.str(`checkGameVersion`), + url: v.str(`${config.selfUrl}/needlessAuth/CheckGameVersion`), + }, + { + service_name: v.str(`checkPlayData`), + url: v.str(`${config.selfUrl}/needAuth/CheckPlayData`), + }, + { + service_name: v.str(`checkPlayableMusicList`), + url: v.str( + `${config.selfUrl}/needAuth/CheckPlayableMusicList`, + ), + }, + { + service_name: v.str(`checkSendLogAvailable`), + url: v.str( + `${config.selfUrl}/needlessAuth/CheckSendLogAvailable`, + ), + }, + { + service_name: v.str(`checkTakeover`), + url: v.str(`${config.selfUrl}/needAuth/CheckTakeover`), + }, + { + service_name: v.str(`checkUpdate`), + url: v.str(`${config.selfUrl}/needlessAuth/CheckUpdate`), + }, + { + service_name: v.str(`checkVersion`), + url: v.str( + `${config.selfUrl}/needlessAuth/CheckLauncherVersion`, + ), + }, + { + service_name: v.str(`consumeItem`), + url: v.str(`${config.selfUrl}/needAuth/ConsumeItem`), + }, + { + service_name: v.str(`gameEnd`), + url: v.str(`${config.selfUrl}/needAuth/GameEnd`), + }, + { + service_name: v.str(`getAdvertise`), + url: v.str(`${config.selfUrl}/needlessAuth/GetAdvertise`), + }, + { + service_name: v.str(`getChampionshipInfo`), + url: v.str( + `${config.selfUrl}/needlessAuth/GetChampionshipInfo`, + ), + }, + { + service_name: v.str(`getChampionshipRank`), + url: v.str(`${config.selfUrl}/needAuth/GetChampionshipRank`), + }, + { + service_name: v.str(`getChampionshipRecord`), + url: v.str(`${config.selfUrl}/needAuth/GetChampionshipRecord`), + }, + { + service_name: v.str(`getClearRate`), + url: v.str(`${config.selfUrl}/needlessAuth/GetClearRate`), + }, + { + service_name: v.str(`getCompeScoreData`), + url: v.str(`${config.selfUrl}/needAuth/GetCompeScoreData`), + }, + { + service_name: v.str(`getCustomizeSetting`), + url: v.str(`${config.selfUrl}/needAuth/GetCustomizeSetting`), + }, + { + service_name: v.str(`getDownloadResource`), + url: v.str( + 'https://d1rc4pwxnc0pe0.cloudfront.net/v2/resource/distribution/ondemand', + ), + }, + { + service_name: v.str(`getGoodsList`), + url: v.str(`${config.selfUrl}/needlessAuth/GetGoodsList`), + }, + { + service_name: v.str(`getHash`), + url: v.str(`${config.selfUrl}/needlessAuth/GetHash`), + }, + { + service_name: v.str(`getInformation`), + url: v.str(`${config.selfUrl}/needlessAuth/GetInformation`), + }, + { + service_name: v.str(`getItemList`), + url: v.str(`${config.selfUrl}/needAuth/GetItemList`), + }, + { + service_name: v.str(`getItemNum`), + url: v.str(`${config.selfUrl}/needAuth/GetItemNum`), + }, + { + service_name: v.str(`getMusicData`), + url: v.str(`${config.selfUrl}/needAuth/GetMusicData`), + }, + { + service_name: v.str(`getMusicGhost`), + url: v.str(`${config.selfUrl}/needAuth/GetMusicGhost`), + }, + { + service_name: v.str(`getMusicList`), + url: v.str(`${config.selfUrl}/needAuth/GetMusicList`), + }, + { + service_name: v.str(`getPackageList`), + url: v.str(`${config.selfUrl}/needlessAuth/GetPackageList`), + }, + { + service_name: v.str(`getPlayData`), + url: v.str(`${config.selfUrl}/needAuth/GetPlayData`), + }, + { + service_name: v.str(`getPlayableMusicList`), + url: v.str(`${config.selfUrl}/needAuth/GetPlayableMusicList`), + }, + { + service_name: v.str(`getPointList`), + url: v.str(`${config.selfUrl}/needAuth/GetPointList`), + }, + { + service_name: v.str(`getPrivilegeClient`), + url: v.str(`${config.selfUrl}/needAuth/GetPrivilegeClient`), + }, + { + service_name: v.str(`getPrivilegeServer`), + url: v.str(`${config.selfUrl}/needAuth/GetPrivilegeServer`), + }, + { + service_name: v.str(`getResourceInfo`), + url: v.str(`${config.selfUrl}/needlessAuth/GetFile`), + }, + { + service_name: v.str(`getRival`), + url: v.str(`${config.selfUrl}/needAuth/GetRival`), + }, + { + service_name: v.str(`getRivalChallenge`), + url: v.str(`${config.selfUrl}/needAuth/GetRivalChallenge`), + }, + { + service_name: v.str(`getRivalInfo`), + url: v.str(`${config.selfUrl}/needAuth/GetRivalInfo`), + }, + { + service_name: v.str(`getServerClock`), + url: v.str(`${config.selfUrl}/preProcess/GetServerClock`), + }, + { + service_name: v.str(`getServerState`), + url: v.str(`${config.selfUrl}/preProcess/GetServerState`), + }, + { + service_name: v.str(`getServerValues`), + url: v.str(`${config.selfUrl}/needlessAuth/GetServerValues`), + }, + { + service_name: v.str(`getServices`), + url: v.str(`${config.selfUrl}/preProcess/GetServices`), + }, + { + service_name: v.str(`getTemporaryFiles`), + url: v.str(`${config.selfUrl}/needlessAuth/GetTemporaryFiles`), + }, + { + service_name: v.str(`heartbeat`), + url: v.str(`${config.selfUrl}/needAuth/Heartbeat`), + }, + { + service_name: v.str(`loginEX`), + url: v.str( + 'https://p.eagate.573.jp/game/eac2dx/infinitas/API/page/loginEX.html', + ), + }, + { + service_name: v.str(`registPlayer`), + url: v.str(`${config.selfUrl}/needAuth/RegistPlayer`), + }, + { + service_name: v.str(`reportMusicResult`), + url: v.str(`${config.selfUrl}/needAuth/ReportMusicResult`), + }, + { + service_name: v.str(`reserveChangePoint`), + url: v.str(`${config.selfUrl}/needAuth/ReserveChangePoint`), + }, + { + service_name: v.str(`reserveConsumeItem`), + url: v.str(`${config.selfUrl}/needAuth/ReserveConsumeItem`), + }, + { + service_name: v.str(`saveChampionshipRecord`), + url: v.str( + `${config.selfUrl}/needAuth/SaveChampionshipRecord`, + ), + }, + { + service_name: v.str(`savePlayData`), + url: v.str(`${config.selfUrl}/needAuth/SavePlayData`), + }, + { + service_name: v.str(`sendGradeCertificationLog`), + url: v.str( + `${config.selfUrl}/needAuth/SendGradeCertificationLog`, + ), + }, + { + service_name: v.str(`sendLog`), + url: v.str(`${config.selfUrl}/needlessAuth/SendLog`), + }, + { + service_name: v.str(`unlockMusic`), + url: v.str(`${config.selfUrl}/needAuth/UnlockMusic`), + }, + { + service_name: v.str(`uploadFile`), + url: v.str( + `${config.selfUrl}/needlessAuth/UploadFileMultipart`, + ), + }, + { + service_name: v.str(`urlAgreement`), + url: v.str( + 'https://p.eagate.573.jp/game/infinitas/2/jump/agreement.html', + ), + }, + { + service_name: v.str(`urlEaShop`), + url: v.str('https://p.eagate.573.jp/game/eac2dx/jump/eashop.html'), + }, + { + service_name: v.str(`urlKAC5th`), + url: v.str( + 'http://p.eagate.573.jp/game/kac5th/detail.html?id=infinitas&group=outline&lang=ja', + ), + }, + { + service_name: v.str(`usePoint`), + url: v.str(`${config.selfUrl}/needAuth/UsePoint`), + }, + { + service_name: v.str(`usePointDirect`), + url: v.str(`${config.selfUrl}/needAuth/UsePointDirect`), + }, + ], + }, + }; + } +} diff --git a/src/services/package/index.ts b/src/services/package/index.ts deleted file mode 100644 index 70327f7..0000000 --- a/src/services/package/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DefaultService } from '../default.js'; - -export default class extends DefaultService { - list = this.default; -} diff --git a/src/services/pcbevent/index.ts b/src/services/pcbevent/index.ts deleted file mode 100644 index 0301c6f..0000000 --- a/src/services/pcbevent/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DefaultService } from '../default.js'; - -export default class extends DefaultService { - put = this.default; -} diff --git a/src/services/pcbtracker/index.ts b/src/services/pcbtracker/index.ts deleted file mode 100644 index b432404..0000000 --- a/src/services/pcbtracker/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { serviceKxml } from '../../decorators/to-kxml.js'; - -const resp = serviceKxml('pcbtracker'); - -export default class { - @resp('alive') - async alive(): Promise { - return { - $ecenable: 1, - $expire: 3600, - $status: 0, - $time: Math.floor(new Date().valueOf() / 1000), - }; - } -} diff --git a/src/services/services/index.ts b/src/services/services/index.ts deleted file mode 100644 index 5acea7d..0000000 --- a/src/services/services/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { singleton } from 'tsyringe'; -import config from '../../utils/config.js'; -import { serviceKxml } from '../../decorators/to-kxml.js'; - -const resp = serviceKxml('services'); - -@singleton() -export default class { - @resp('get') - async get() { - const selfServices = [ - 'services', 'dlstatus', 'cardmng', - 'eacoin', 'userdata', 'userid', 'pcbtracker', 'pcbevent', - 'message', 'facility', 'apsmanager', 'sidmgr', 'package', - 'uploaders', 'local', 'local2', 'lobby', - ] - - return { - $expire: 43200, - $mode: 'operation', - $product_domain: 1, - $statuc: 0, - item: [ - { - $name: 'keepalive', - $url: 'ping://127.0.0.1/?pa=127.0.0.1&ia=127.0.0.1&ga=127.0.0.1ma=127.0.0.1&t1=2&t2=15&rt=2', - }, - { - $name: 'ntp', - $url: config.ntpUrl, - }, - ...selfServices.map(v => ({ - $name: v, - $url: config.selfUrl, - })) - ] - }; - } -} diff --git a/src/types.ts b/src/types.ts index 7e95b9b..5438bef 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,9 +2,7 @@ import { Logger } from '@cordisjs/logger'; import { DefaultState, ParameterizedContext } from 'koa'; export interface ILaochanContext { - model?: string; - tag?: string; - pcbid?: string; + token?: string; service: { name: string, @@ -12,10 +10,7 @@ export interface ILaochanContext { }; logger: Logger; - - request: { - body: Buffer | object | undefined; - }; + body?: unknown; } export type Context = ParameterizedContext; diff --git a/src/utils/config.ts b/src/utils/config.ts index c1ea147..a53cb26 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -3,11 +3,11 @@ dotenv.config(); class Config { get port() { - return process.env.PORT ? parseInt(process.env.PORT) : 10573; + return process.env.PORT ? parseInt(process.env.PORT) : 25730; } get selfUrl() { - return process.env.SELF_URL ?? 'http://127.0.0.1:10573'; + return process.env.SELF_URL ?? 'http://localhost:25730'; } get ntpUrl() { @@ -15,11 +15,11 @@ class Config { } get mongoUrl() { - return process.env.MONGO_URL ?? 'mongodb://127.0.0.1:27017'; + return process.env.MONGO_URL ?? 'mongodb://localhost:27017'; } get dbName() { - return process.env.DB_NAME ?? 'laochan-net'; + return process.env.DB_NAME ?? 'laochan-eacnet'; } get isDev() { diff --git a/src/utils/kxml-value.ts b/src/utils/kxml-value.ts index 619d2b3..104ca85 100644 --- a/src/utils/kxml-value.ts +++ b/src/utils/kxml-value.ts @@ -27,7 +27,7 @@ function vg(type: ValueTypes) { } } - return attrs; + return result; }; } diff --git a/src/utils/lz77.ts b/src/utils/lz77.ts index 412cf28..2884ef6 100644 --- a/src/utils/lz77.ts +++ b/src/utils/lz77.ts @@ -1,37 +1,64 @@ -export function lz77Decode(data: Buffer): Buffer { - let offset = 0; - const output: number[] = []; - - while (offset < data.length) { - const flag = data[offset]; - offset += 1; - - for (let bit = 0; bit < 8; bit++) { - if (flag & (1 << bit)) { - output.push(data[offset]); - offset += 1; - } else { - if (offset >= data.length) break; - - const lookbackFlag = data.readUInt16BE(offset); - const lookbackLength = (lookbackFlag & 0x000f) + 3; - const lookbackOffset = lookbackFlag >> 4; - - offset += 2; - if (lookbackFlag == 0) break; - - for (let i = 0; i < lookbackLength; i++) { - const loffset = output.length - lookbackOffset; - - if (loffset <= 0 || loffset >= output.length) { - output.push(0); - } else { - output.push(output[loffset]); - } +export class LZ77 { + static compress(input: ArrayLike) { + const a = Uint8Array.from(input); + const res = new Uint8Array(a.length + a.length / 8 + 3); + let p = 0; + for (let i = 0; i < a.length; ++i) { + if (i % 8 === 0) { + if (a.length - i < 8) { + res[p] = (Math.pow(2, a.length - i) - 1); + } else { + res[p] = 255; } + p++; } + res[p] = a[i]; + p++; } + res[p] = 0; + res[p + 1] = 0; + + return Buffer.from(res); } - return Buffer.from(output); + static decompress(a: ArrayLike) { + const res = new Uint8Array(0x190000); + let p = 0; + let r = 0; + let t = 0; + let b = 8; + let mask = 0; + + while (true) { + if (b === 8) { + mask = a[p]; + p++; + b = 0; + } + if ((mask & 1) === 1) { + res[r] = a[p]; + r++; + p++; + } else { + let distance = a[p]; + let count = a[p + 1]; + if (distance === 0 && count === 0) break; + p += 2; + distance <<= 4; + distance |= count >> 4; + count = (count & 0x0F) + 3; + t = r - distance; + for (let i = 0; i < count; ++i) { + res[r] = t < 0 ? 0x00 : res[t]; + r++; + t++; + } + } + mask >>= 1; + b += 1; + } + + const o = res.slice(0, r); + return Buffer.from(o); + } } diff --git a/templates/facility/get.pug b/templates/facility/get.pug deleted file mode 100644 index b250886..0000000 --- a/templates/facility/get.pug +++ /dev/null @@ -1,47 +0,0 @@ -response - facility(method="get", status=status) - location - id(__type="str")= location.id - type(__type="u8")= location.type - country(__type="str")= location.country - region(__type="str")= location.region - name(__type="str")= location.name - countryname(__type="str")= location.countryName - countryjname(__type="str")= location.countryJName - regionname(__type="str")= location.regionName - regionjname(__type="str")= location.regionJName - customercode(__type="str")= location.customerCode - companycode(__type="str")= location.companyCode - latitude(__type="s32")= location.latitude - longitude(__type="s32")= location.longitude - accuracy(__type="u8")= location.accuracy - line - id(__type="str")= line.id - class(__type="u8")= line.class - upclass(__type="u8")= line.upClass - rtt(__type="u16")= line.rtt - portfw - globalip(__type="ip4")= portfw.globalIp - globalport(__type="u16")= portfw.globalPort - privateport(__type="u16")= portfw.privatePort - public - flag(__type="u8")= public.flag - name(__type="str")= public.name - latitude(__type="str")= public.latitude - longitude(__type="str")= public.longitude - share - url - epass(__type="str")= share.url - arcadefan(__type="str")= share.url - konaminetdx(__type="str")= share.url - konamiid(__type="str")= share.url - eagate(__type="str")= share.url - eapass - valid(__type="u16")= share.eapass - eacoin - notchamount(__type="s32")= share.eacoin.notchAmount - notchcount(__type="s32")= share.eacoin.notchCount - supplylimit(__type="s32")= share.eacoin.supplyLimit - calendar - year(__type="s16")= calendar.year - holiday(__type="s16" __count=calendar.holiday.length)= calendar.holiday.join(' ') diff --git a/tsconfig.json b/tsconfig.json index d37dfe4..0805f36 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "node16", "lib": ["ES2022"], "moduleResolution": "node16", - "rootDir": ".", + "rootDir": "./src", "outDir": "build", "allowSyntheticDefaultImports": true, "importHelpers": true,