From 868d3fd3bde7f7ef246523fb4363d2cb7057ade3 Mon Sep 17 00:00:00 2001 From: Rin Date: Tue, 12 Jul 2022 11:42:38 +0100 Subject: [PATCH] Add mucha & allnet emulation in bayshore --- .gitignore | 3 +- config.example.json | 4 ++ package.json | 3 ++ src/allnet.ts | 102 ++++++++++++++++++++++++++++++++++++++++++++ src/config.ts | 21 +++++++++ src/index.ts | 45 ++++++++++++++++--- src/mucha.ts | 92 +++++++++++++++++++++++++++++++++++++++ yarn.lock | 19 ++++++++- 8 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 config.example.json create mode 100644 src/allnet.ts create mode 100644 src/config.ts create mode 100644 src/mucha.ts diff --git a/.gitignore b/.gitignore index 822e154..16f0f8c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ proto/ dist/ server_wangan.key cert.pfx -key.pem \ No newline at end of file +key.pem +config.json \ No newline at end of file diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..2941428 --- /dev/null +++ b/config.example.json @@ -0,0 +1,4 @@ +{ + "shopName": "Bayshore", + "shopNickname": "BSH" +} \ No newline at end of file diff --git a/package.json b/package.json index 8f2d38b..338c65c 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,9 @@ "@types/pem": "^1.9.6", "body-parser": "^1.20.0", "express": "^4.18.1", + "form-urlencoded": "^6.0.6", + "iconv-lite": "^0.6.3", + "moment": "^2.29.4", "pem": "^1.14.6", "protobufjs": "^7.0.0", "ts-proto": "^1.117.0" diff --git a/src/allnet.ts b/src/allnet.ts new file mode 100644 index 0000000..960b1b6 --- /dev/null +++ b/src/allnet.ts @@ -0,0 +1,102 @@ +import bodyParser from "body-parser"; +import { Application } from "express"; +import { unzipSync } from "zlib"; +import { Module } from "./module"; +import iconv from "iconv-lite"; +import { Config } from "./config"; + +// TODO: Move this into the config +const STARTUP_URI = "https://localhost:9002"; +const STARTUP_HOST = "localhost:9002"; + +export default class AllnetModule extends Module { + register(app: Application): void { + app.use(bodyParser.raw({ + type: '*/*' + })); + + app.use("/sys/servlet/PowerOn", async function(req, res, next) { + if (req.method !== "POST") { + return res.status(405).end(); + } + + if (!req.is("application/x-www-form-urlencoded")) { + return next(); + } + + const base64 = req.body.toString('ascii'); + const zbytes = Buffer.from(base64, "base64"); + const bytes = unzipSync(zbytes); + const str = bytes.toString("ascii").trim(); + + const kvps = str.split("&"); + const reqParams: any = {}; + + // Keys and values are not URL-escaped + + kvps.forEach(kvp => { + const [key, val] = kvp.split("="); + + reqParams[key] = val; + }); + + const send_ = res.send; + + req.body = reqParams; + res.send = resParams => { + const str = + Object.entries(resParams) + .map(([key, val]) => key + "=" + val) + .join("&") + "\n"; + + res.set("content-type", "text/plain"); + + const bin = iconv.encode(str, "shift_jis"); + + return send_.apply(res, [bin]); + }; + + return next(); + }); + + app.post("/sys/servlet/PowerOn", function(req, res) { + console.log('ALL.net: Startup request'); + + // Cut milliseconds out of ISO timestamp + + const now = new Date(); + const adjusted = now; + + let shopName = Config.getConfig().shopName; + let shopNick = Config.getConfig().shopNickname; + + const resParams = { + stat: 1, + uri: STARTUP_URI, + host: STARTUP_HOST, + place_id: "JPN0123", + name: shopName, + nickname: shopNick, + region0: "1", + region_name0: "W", + region_name1: "X", + region_name2: "Y", + region_name3: "Z", + country: "JPN", + allnet_id: "456", + timezone: "002:00", + setting: "", + year: adjusted.getFullYear(), + month: adjusted.getMonth() + 1, // I hate JS + day: adjusted.getDate(), + hour: adjusted.getHours(), + minute: adjusted.getMinutes(), + second: adjusted.getSeconds(), + res_class: "PowerOnResponseVer2", + token: req.body.token, + }; + + res.send(resParams); + }); + } +} \ No newline at end of file diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..bac0e2a --- /dev/null +++ b/src/config.ts @@ -0,0 +1,21 @@ +import fs from 'fs'; + +export interface ConfigFile { + shopName: string; + shopNickname: string; +} + +export class Config { + private static cfg: ConfigFile; + + static load() { + console.log('Loading config file...'); + let cfg = fs.readFileSync('./config.json', 'utf-8'); + let json = JSON.parse(cfg); + this.cfg = json as ConfigFile; + } + + static getConfig(): ConfigFile { + return this.cfg; + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 9270ef0..47fcb8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,18 +6,40 @@ import {PrismaClient} from '@prisma/client'; import https, {globalAgent} from 'https'; import fs from 'fs'; import bodyParser from 'body-parser'; +import AllnetModule from './allnet'; +import MuchaModule from './mucha'; +import { Config } from './config'; globalAgent.options.keepAlive = true; // @ts-ignore require('http').globalAgent.options.keepAlive = true; +const PORT_ALLNET = 80; +const PORT_MUCHA = 10082; +const PORT_BNGI = 9002; + +Config.load(); + const app = express(); app.use(bodyParser.raw({ type: '*/*' })); +const muchaApp = express(); +const allnetApp = express(); + app.use((req, res, next) => { - console.log(`${req.method} ${req.url}`); + console.log(`[ MAIN] ${req.method} ${req.url}`); + next() +}); + +muchaApp.use((req, res, next) => { + console.log(`[ MUCHA] ${req.method} ${req.url}`); + next() +}); + +allnetApp.use((req, res, next) => { + console.log(`[ALLNET] ${req.method} ${req.url}`); next() }); @@ -34,9 +56,20 @@ app.all('*', (req, res) => { res.status(200).end(); }) -https.createServer({ - key: fs.readFileSync('./server_wangan.key'), - cert: fs.readFileSync('./server_wangan.crt') -}, app).listen(9002, () => { - console.log('Server listening on port 9002!'); +new AllnetModule().register(allnetApp); +new MuchaModule().register(muchaApp); + +let key = fs.readFileSync('./server_wangan.key'); +let cert = fs.readFileSync('./server_wangan.crt'); + +https.createServer({key, cert}, allnetApp).listen(PORT_ALLNET, () => { + console.log(`ALL.net server listening on port ${PORT_ALLNET}!`); +}) + +https.createServer({key, cert}, muchaApp).listen(PORT_MUCHA, () => { + console.log(`Mucha server listening on port ${PORT_MUCHA}!`); +}) + +https.createServer({key, cert}, app).listen(PORT_BNGI, () => { + console.log(`Game server listening on port ${PORT_BNGI}!`); }) diff --git a/src/mucha.ts b/src/mucha.ts new file mode 100644 index 0000000..97779ac --- /dev/null +++ b/src/mucha.ts @@ -0,0 +1,92 @@ +import express, { Application } from "express"; +import formUrlEncoded from "form-urlencoded"; +import moment from "moment"; +import { Config } from "./config"; +import { Module } from "./module"; + +const PORT = 10082; + +export default class MuchaModule extends Module { + register(app: Application): void { + const URL_BASE = `https://localhost:${PORT}` + + app.use(express.urlencoded({ + type: '*/*', + extended: true + })) + + app.post('/updatacheck.do', (req, res) => { + let response = { + RESULTS: "001", + UPDATE_VER_1: req.body.gameVer, + UPDATE_URL_1: `${URL_BASE}/updUrl1/`, + UPDATE_SIZE_1: 9318000, + UPDATE_CRC_1: "55C4000000000000", + CHECK_URL_1: `${URL_BASE}/checkUrl/`, + EXE_VER_1: req.body.gameVer, + INFO_SIZE_1: 180, + COM_SIZE_1: 16384, + COM_TIME_1: 100, + LAN_INFO_SIZE_1: 180 + } + + let urlResponse = formUrlEncoded(response); + let decResponse = decodeURIComponent(urlResponse); + + res.status(200).send(decResponse); + }) + + app.post('/boardauth.do', (req, res) => { + let serverTime = moment().format('YYYYMMDDHHmm'); + let utcServerTime = moment().utc().format('YYYYMMDDHHmm'); + + let shopName = Config.getConfig().shopName; + let shopNick = Config.getConfig().shopNickname; + + let response = { + RESULTS: "001", + AREA_0: "008", + AREA_0_EN: "", + AREA_1: "009", + AREA_1_EN: "", + AREA_2: "010", + AREA_2_EN: "", + AREA_3: "011", + AREA_3_EN: "", + AREA_FULL_0: "", + AREA_FULL_0_EN: "", + AREA_FULL_1: "", + AREA_FULL_1_EN: "", + AREA_FULL_2: "", + AREA_FULL_2_EN: "", + AREA_FULL_3: "", + AREA_FULL_3_EN: "", + AUTH_INTERVAL: "86400", + CHARGE_URL: `${URL_BASE}/charge/`, + CONSUME_TOKEN: "0", + COUNTRY_CD: "JPN", + DONGLE_FLG: "1", + EXPIRATION_DATE: "null", + FILE_URL: `${URL_BASE}/file/`, + FORCE_BOOT: "0", + PLACE_ID: req.body.placeId, + PREFECTURE_ID: "14", + SERVER_TIME: serverTime, + UTC_SERVER_TIME: utcServerTime, + SHOP_NAME: shopName, + SHOP_NAME_EN: shopName, + SHOP_NICKNAME: shopNick, + SHOP_NICKNAME_EN: shopNick, + URL_1: `${URL_BASE}/url1/`, + URL_2: `${URL_BASE}/url2/`, + URL_3: `${URL_BASE}/url3/`, + USE_TOKEN: "0" + } + + let urlResponse = formUrlEncoded(response); + let decResponse = decodeURIComponent(urlResponse); + + res.status(200).send(decResponse); + }) + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 1943669..c144c65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -557,6 +557,11 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" +form-urlencoded@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/form-urlencoded/-/form-urlencoded-6.0.6.tgz#6505aca762436f90f2a736f79c24ad30787daacc" + integrity sha512-5n3L86l3uVJLFk8w+HTcuaV8WrEeH9pPqJcICxAbs3oW/gsKg9kJ8XVPZ3I1PJR50ld2fQjstT94p4G90JDMAg== + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -644,6 +649,13 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -838,6 +850,11 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -1039,7 +1056,7 @@ safe-buffer@5.2.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==