Apply Prettier

This commit is contained in:
Tau
2019-03-07 23:23:25 -05:00
parent f667b6ee6e
commit 1337fc21f3
9 changed files with 480 additions and 400 deletions

View File

@@ -1,4 +1,4 @@
const { Transform } = require('stream')
const { Transform } = require("stream");
class Decoder extends Transform {
constructor(options) {
@@ -6,84 +6,91 @@ class Decoder extends Transform {
readableObjectMode: true,
writableObjectMode: true,
...options,
})
});
}
static ident(chunk) {
const gameId = chunk.toString('ascii', 0x000A, 0x000E)
const keychipId = chunk.toString('ascii', 0x0014, 0x001F)
const gameId = chunk.toString("ascii", 0x000a, 0x000e);
const keychipId = chunk.toString("ascii", 0x0014, 0x001f);
return { gameId, keychipId }
return { gameId, keychipId };
}
static cardId(chunk) {
const luid = chunk.slice(0x0020, 0x002A)
const luid = chunk.slice(0x0020, 0x002a);
return { luid, ...Decoder.ident(chunk) }
return { luid, ...Decoder.ident(chunk) };
}
static log(chunk) {
// idk what any of this stuff means yet
// field20 and field28 appear to be an aime id but that is all.
const field20 = chunk.readUInt32LE(0x20)
const field24 = chunk.readUInt32LE(0x24)
const field28 = chunk.readUInt32LE(0x28)
const field2C = chunk.readUInt32LE(0x2C)
const field30 = chunk.readUInt32LE(0x30)
const field34 = chunk.readUInt32LE(0x34)
const field38 = chunk.readUInt32LE(0x38)
const field3C = chunk.readUInt32LE(0x3C)
const field20 = chunk.readUInt32LE(0x20);
const field24 = chunk.readUInt32LE(0x24);
const field28 = chunk.readUInt32LE(0x28);
const field2C = chunk.readUInt32LE(0x2c);
const field30 = chunk.readUInt32LE(0x30);
const field34 = chunk.readUInt32LE(0x34);
const field38 = chunk.readUInt32LE(0x38);
const field3C = chunk.readUInt32LE(0x3c);
return {
field20, field24, field28, field2C, field30, field34, field38, field3C,
...Decoder.ident(chunk)
}
field20,
field24,
field28,
field2C,
field30,
field34,
field38,
field3C,
...Decoder.ident(chunk),
};
}
_transform(chunk, encoding, callback) {
const cmd = chunk.readUInt16LE(0x04)
const cmd = chunk.readUInt16LE(0x04);
switch (cmd) {
case 0x0005:
return callback(null, {
cmd: 'register',
cmd: "register",
...Decoder.cardId(chunk),
})
});
case 0x0009:
return callback(null, {
cmd: 'log',
cmd: "log",
...Decoder.log(chunk),
})
});
case 0x000B:
case 0x000b:
return callback(null, {
cmd: 'campaign',
cmd: "campaign",
...Decoder.ident(chunk),
})
});
case 0x000F:
case 0x000f:
return callback(null, {
cmd: 'lookup',
cmd: "lookup",
...Decoder.cardId(chunk),
})
});
case 0x0064:
return callback(null, {
cmd: 'hello',
...Decoder.ident(chunk)
})
cmd: "hello",
...Decoder.ident(chunk),
});
case 0x0066:
return callback(null, {
cmd: 'goodbye',
})
cmd: "goodbye",
});
default:
return callback(null, {
cmd: `unknown_${cmd}`,
})
});
}
}
}
@@ -94,78 +101,78 @@ class Encoder extends Transform {
readableObjectMode: true,
writableObjectMode: true,
...options,
})
});
}
static init(length) {
const buf = Buffer.alloc(length)
const buf = Buffer.alloc(length);
buf.writeUInt16LE(0xA13E, 0x0000) // Magic?
buf.writeUInt16LE(0x3087, 0x0002) // ???
buf.writeUInt16LE(length, 0x0006)
buf.writeUInt16LE(0xa13e, 0x0000); // Magic?
buf.writeUInt16LE(0x3087, 0x0002); // ???
buf.writeUInt16LE(length, 0x0006);
return buf
return buf;
}
_transform(chunk, encoding, callback) {
console.log('Aimedb: Encode', chunk)
console.log("Aimedb: Encode", chunk);
let buf
let buf;
switch (chunk.cmd) {
case 'hello':
buf = Encoder.init(0x0020)
buf.writeUInt16LE(0x0065, 0x0004) // cmd code
buf.writeUInt16LE(chunk.status, 0x0008)
case "hello":
buf = Encoder.init(0x0020);
buf.writeUInt16LE(0x0065, 0x0004); // cmd code
buf.writeUInt16LE(chunk.status, 0x0008);
break
break;
case 'campaign':
case "campaign":
// Still figuring this out...
buf = Encoder.init(0x0200)
buf.writeUInt16LE(0x000C, 0x0004) // cmd code
buf.writeUInt16LE(chunk.status, 0x0008)
buf = Encoder.init(0x0200);
buf.writeUInt16LE(0x000c, 0x0004); // cmd code
buf.writeUInt16LE(chunk.status, 0x0008);
// Campaign array starts at 0x20
// Element size is 0xA0
break
break;
case 'lookup':
case "lookup":
// -1 aime id means card is not registered
// register level does not seem to matter
buf = Encoder.init(0x0130)
buf.writeUInt16LE(0x0010, 0x0004) // cmd code
buf.writeUInt16LE(chunk.status, 0x0008)
buf.writeInt32LE(chunk.aimeId || -1, 0x0020)
buf.writeUInt8(Encoder.registerLevels[chunk.registerLevel], 0x0024)
buf = Encoder.init(0x0130);
buf.writeUInt16LE(0x0010, 0x0004); // cmd code
buf.writeUInt16LE(chunk.status, 0x0008);
buf.writeInt32LE(chunk.aimeId || -1, 0x0020);
buf.writeUInt8(Encoder.registerLevels[chunk.registerLevel], 0x0024);
break
break;
case 'register':
buf = Encoder.init(0x0030)
buf.writeUInt16LE(0x0006, 0x0004) // cmd code
buf.writeUInt16LE(chunk.status, 0x0008)
buf.writeInt32LE(chunk.aimeId, 0x0020)
case "register":
buf = Encoder.init(0x0030);
buf.writeUInt16LE(0x0006, 0x0004); // cmd code
buf.writeUInt16LE(chunk.status, 0x0008);
buf.writeInt32LE(chunk.aimeId, 0x0020);
break
break;
case 'log':
buf = Encoder.init(0x0020)
buf.writeUInt16LE(0x000A, 0x0004) // cmd code
buf.writeUInt16LE(chunk.status, 0x0008)
case "log":
buf = Encoder.init(0x0020);
buf.writeUInt16LE(0x000a, 0x0004); // cmd code
buf.writeUInt16LE(chunk.status, 0x0008);
break
break;
default:
return callback(new Error(`Unimplemented response: ${cmd}`))
return callback(new Error(`Unimplemented response: ${cmd}`));
}
console.log('Aimedb: Send', buf)
console.log("Aimedb: Send", buf);
return callback(null, buf)
return callback(null, buf);
}
}
@@ -173,9 +180,9 @@ Encoder.registerLevels = {
none: 0,
portal: 1,
segaid: 2,
}
};
module.exports = {
Decoder,
Encoder,
}
};

View File

@@ -1,47 +1,114 @@
const { Decoder } = require('./cmd')
const { Decoder } = require("./cmd");
function decode(req) {
const decoder = new Decoder()
const decoder = new Decoder();
decoder.write(req)
decoder.write(req);
return decoder.read()
return decoder.read();
}
test('decode hello', () => {
test("decode hello", () => {
const req = Buffer.from([
0x3e, 0xa1, 0x87, 0x30, 0x64, 0x00, 0x20, 0x00,
0x00, 0x00, 0x53, 0x44, 0x42, 0x54, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x41, 0x36, 0x39, 0x45,
0x30, 0x31, 0x41, 0x39, 0x39, 0x39, 0x39, 0x00,
])
0x3e,
0xa1,
0x87,
0x30,
0x64,
0x00,
0x20,
0x00,
0x00,
0x00,
0x53,
0x44,
0x42,
0x54,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x41,
0x36,
0x39,
0x45,
0x30,
0x31,
0x41,
0x39,
0x39,
0x39,
0x39,
0x00,
]);
const obj = decode(req)
const obj = decode(req);
expect(obj.cmd).toBe('hello')
expect(obj.gameId).toBe('SDBT')
expect(obj.keychipId).toBe('A69E01A9999')
})
expect(obj.cmd).toBe("hello");
expect(obj.gameId).toBe("SDBT");
expect(obj.keychipId).toBe("A69E01A9999");
});
test('decode lookup', () => {
test("decode lookup", () => {
const req = Buffer.from([
0x3e, 0xa1, 0x87, 0x30, 0x0f, 0x00, 0x30, 0x00,
0x00, 0x00, 0x53, 0x44, 0x42, 0x54, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x41, 0x36, 0x39, 0x45,
0x30, 0x31, 0x41, 0x39, 0x39, 0x39, 0x39, 0x00,
0x01, 0x03, 0x64, 0x95, 0x85, 0x05, 0x23, 0x03,
0x06, 0x76, 0x01, 0x02, 0x22, 0xc6, 0x4e, 0x00,
])
0x3e,
0xa1,
0x87,
0x30,
0x0f,
0x00,
0x30,
0x00,
0x00,
0x00,
0x53,
0x44,
0x42,
0x54,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x41,
0x36,
0x39,
0x45,
0x30,
0x31,
0x41,
0x39,
0x39,
0x39,
0x39,
0x00,
0x01,
0x03,
0x64,
0x95,
0x85,
0x05,
0x23,
0x03,
0x06,
0x76,
0x01,
0x02,
0x22,
0xc6,
0x4e,
0x00,
]);
const obj = decode(req)
const obj = decode(req);
expect(obj.cmd).toBe('lookup')
expect(obj.gameId).toBe('SDBT')
expect(obj.keychipId).toBe('A69E01A9999')
expect(obj.cmd).toBe("lookup");
expect(obj.gameId).toBe("SDBT");
expect(obj.keychipId).toBe("A69E01A9999");
expect(obj.luid).toEqual(
Buffer.from([
0x01, 0x03, 0x64, 0x95, 0x85, 0x05, 0x23, 0x03,
0x06, 0x76,
])
)
})
Buffer.from([0x01, 0x03, 0x64, 0x95, 0x85, 0x05, 0x23, 0x03, 0x06, 0x76])
);
});

View File

@@ -1,44 +1,44 @@
const { Transform } = require('stream')
const { Transform } = require("stream");
class Deframer extends Transform {
constructor(options) {
super({
readableObjectMode: true,
...options,
})
});
this.state = Buffer.alloc(0)
this.state = Buffer.alloc(0);
}
_transform(chunk, encoding, callback) {
this.state = Buffer.concat([ this.state, chunk ])
this.state = Buffer.concat([this.state, chunk]);
if (this.state.length < 8) {
return
return;
}
const magic = this.state.readUInt16LE(0x0000)
const magic = this.state.readUInt16LE(0x0000);
if (magic != 0xA13E) {
return callback(new Error(`Invalid magic (decimal ${magic})`))
if (magic != 0xa13e) {
return callback(new Error(`Invalid magic (decimal ${magic})`));
}
const len = this.state.readUInt16LE(0x0006)
const len = this.state.readUInt16LE(0x0006);
if (this.state.length < len) {
return
return;
}
const frame = this.state.slice(0, len)
const frame = this.state.slice(0, len);
console.log('Aimedb: Recv', frame)
console.log("Aimedb: Recv", frame);
this.state = this.state.slice(len)
this.state = this.state.slice(len);
return callback(null, frame)
return callback(null, frame);
}
}
module.exports = {
Deframer,
}
};

View File

@@ -1,66 +1,66 @@
const setup = require('./pipeline')
const setup = require("./pipeline");
async function aimedb(socket) {
console.log('Aimedb: Connection opened')
console.log("Aimedb: Connection opened");
const { input, output } = setup(socket)
const { input, output } = setup(socket);
try {
for await (const req of input) {
console.log('Aimedb: Decode', req)
console.log("Aimedb: Decode", req);
const { cmd } = req
const { cmd } = req;
switch (cmd) {
case 'hello':
console.log('Aimedb: Hello')
output.write({ cmd, status: 1 })
case "hello":
console.log("Aimedb: Hello");
output.write({ cmd, status: 1 });
break
break;
case 'campaign':
console.log('Aimedb: Campaign stuff')
output.write({ cmd, status: 1 })
case "campaign":
console.log("Aimedb: Campaign stuff");
output.write({ cmd, status: 1 });
break
break;
case 'lookup':
console.log('Aimedb: Mifare lookup', req.luid)
output.write({ cmd, status: 1 }) // Add aimeId if desired
case "lookup":
console.log("Aimedb: Mifare lookup", req.luid);
output.write({ cmd, status: 1 }); // Add aimeId if desired
break
break;
case 'register':
case "register":
// We get sent here if lookup does not return an aimeId
console.log('Aimedb: Mifare register', req.luid)
output.write({ cmd, status: 1, aimeId: 12345678 })
console.log("Aimedb: Mifare register", req.luid);
output.write({ cmd, status: 1, aimeId: 12345678 });
break
break;
case 'log':
console.log('Aimedb: Log message')
output.write({ cmd, status: 1 })
case "log":
console.log("Aimedb: Log message");
output.write({ cmd, status: 1 });
break
break;
case 'goodbye':
console.log('Aimedb: Goodbye')
case "goodbye":
console.log("Aimedb: Goodbye");
break
break;
default:
console.log('Aimedb: Handler not implemented!')
console.log("Aimedb: Handler not implemented!");
break
break;
}
}
} catch (e) {
console.log('Aimedb: Connection error:\n', e)
console.log("Aimedb: Connection error:\n", e);
}
console.log('Aimedb: Connection closed\n')
socket.end()
console.log("Aimedb: Connection closed\n");
socket.end();
}
module.exports = aimedb
module.exports = aimedb;

View File

@@ -1,32 +1,28 @@
const crypto = require('crypto')
const { pipeline } = require('stream')
const crypto = require("crypto");
const { pipeline } = require("stream");
const { Deframer } = require('./frame')
const { Decoder, Encoder } = require('./cmd')
const { Deframer } = require("./frame");
const { Decoder, Encoder } = require("./cmd");
const K = Buffer.from('Copyright(C)SEGA', 'utf8')
const K = Buffer.from("Copyright(C)SEGA", "utf8");
function setup(socket) {
const input = pipeline(
socket,
crypto
.createDecipheriv('aes-128-ecb', K, null)
.setAutoPadding(false),
crypto.createDecipheriv("aes-128-ecb", K, null).setAutoPadding(false),
new Deframer(),
new Decoder(),
)
new Decoder()
);
const output = new Encoder()
const output = new Encoder();
pipeline(
output,
crypto
.createCipheriv('aes-128-ecb', K, null)
.setAutoPadding(false),
socket,
)
crypto.createCipheriv("aes-128-ecb", K, null).setAutoPadding(false),
socket
);
return { input, output }
return { input, output };
}
module.exports = setup
module.exports = setup;

View File

@@ -1,77 +1,79 @@
const bodyParser = require('body-parser')
const express = require('express')
const crypto = require('crypto')
const zlib = require('zlib')
const fs = require('fs')
const bodyParser = require("body-parser");
const express = require("express");
const crypto = require("crypto");
const zlib = require("zlib");
const fs = require("fs");
const billingKeyPair = fs.readFileSync('pki/billing.key')
const billingKeyPair = fs.readFileSync("pki/billing.key");
const playlimit = 1024
const nearfull = 0x00010200
const playlimit = 1024;
const nearfull = 0x00010200;
// nearfull: high 16 bits is billing mode, low 16 bits is actual nearfull val.
const app = express()
const app = express();
function decodeRequest(buf) {
const reqBytes = zlib.inflateRawSync(buf)
const reqStr = reqBytes.toString()
const reqBytes = zlib.inflateRawSync(buf);
const reqStr = reqBytes.toString();
return reqStr.trim().split('\r\n').map(line => {
// Crack key-value pairs
return reqStr
.trim()
.split("\r\n")
.map(line => {
// Crack key-value pairs
const params = {}
const params = {};
for (const kvp of line.split('&')) {
const [ key, val ] = kvp.split('=')
params[key] = val
}
for (const kvp of line.split("&")) {
const [key, val] = kvp.split("=");
params[key] = val;
}
return params
})
return params;
});
}
function encodeResponse(items) {
return items.map(
item => Object.entries(
item
).map( ([ key, val ]) =>
key + '=' + val // Keys,vals are not url escaped
).join('&')
).join(
'\r\n'
) + '\r\n'
return (
items
.map(item =>
Object.entries(item)
.map(
([key, val]) => key + "=" + val // Keys,vals are not url escaped
)
.join("&")
)
.join("\r\n") + "\r\n"
);
}
app.use(bodyParser.raw())
app.use(bodyParser.raw());
app.post('/request/', function (req, resp) {
const reqItems = decodeRequest(req.body)
app.post("/request/", function(req, resp) {
const reqItems = decodeRequest(req.body);
console.log('\n--- Billing Request ---\n\n', reqItems)
console.log("\n--- Billing Request ---\n\n", reqItems);
const first = reqItems[0]
const first = reqItems[0];
// Do cryptographic signatures
const [
playlimitsig, nearfullsig
] = [
playlimit, nearfull
].map(value => {
const buf = Buffer.alloc(15)
const [playlimitsig, nearfullsig] = [playlimit, nearfull].map(value => {
const buf = Buffer.alloc(15);
buf.writeInt32LE(value, 0)
buf.write(first.keychipid, 4)
buf.writeInt32LE(value, 0);
buf.write(first.keychipid, 4);
return crypto.createSign('RSA-SHA1')
.update(buf)
.sign(billingKeyPair, 'hex')
})
return crypto
.createSign("RSA-SHA1")
.update(buf)
.sign(billingKeyPair, "hex");
});
// Assemble other params
const respItems = []
const respItems = [];
respItems.push({
// 0 or 6 is success, anything else is an error
@@ -85,7 +87,7 @@ app.post('/request/', function (req, resp) {
linelimit: 1,
// Server error message, copied to ALLNet debug log
message: '',
message: "",
// Keychip odometer limit. Game will lock out if the odometer reaches
// this value
@@ -95,7 +97,7 @@ app.post('/request/', function (req, resp) {
playlimitsig,
// idk
protocolver: '1.000',
protocolver: "1.000",
// A compound of two values. High 16 bits is the billing mode, low 16 bits
// is the actual "nearfull" value. Not sure exactly what nearfull is, at a
@@ -122,15 +124,15 @@ app.post('/request/', function (req, resp) {
// Month 2018/09: 123 total plays
// Month 2018/10: 456 total plays
// Month 2018/11: 789 total plays
playhistory: '000000/0:000000/0:000000/0',
})
playhistory: "000000/0:000000/0:000000/0",
});
console.log('\n--- Billing Response ---\n\n', respItems)
console.log("\n--- Billing Response ---\n\n", respItems);
const respStr = encodeResponse(respItems)
const respStr = encodeResponse(respItems);
resp.set('content-type', 'text/plain')
resp.send(respStr)
})
resp.set("content-type", "text/plain");
resp.send(respStr);
});
module.exports = app
module.exports = app;

View File

@@ -1,107 +1,107 @@
const express = require('express')
const zlib = require('zlib')
const express = require("express");
const zlib = require("zlib");
const app = express()
const app = express();
// Standard inbound JSON deserialization
app.use(express.json())
app.use(express.json());
// Custom outbound JSON serialization that forces compression. Client tries to
// inflate the response whether you have a Transfer-Encoding header or not -.-
app.use(function (req, resp, next) {
resp.json = function (obj) {
const str = JSON.stringify(obj)
const buf = Buffer.from(str)
const comp = zlib.deflateSync(buf)
app.use(function(req, resp, next) {
resp.json = function(obj) {
const str = JSON.stringify(obj);
const buf = Buffer.from(str);
const comp = zlib.deflateSync(buf);
resp.set('Content-Type', 'application/json')
resp.set('Content-Length', comp.length)
resp.set('Transfer-Encoding', 'deflate')
resp.send(comp)
}
resp.set("Content-Type", "application/json");
resp.set("Content-Length", comp.length);
resp.set("Transfer-Encoding", "deflate");
resp.send(comp);
};
next()
})
next();
});
// Trace requests and responses
app.use(function (req, resp, next) {
console.log('\n--- Chunithm %s ---\n', req.url)
console.log('Request:', req.body)
app.use(function(req, resp, next) {
console.log("\n--- Chunithm %s ---\n", req.url);
console.log("Request:", req.body);
const prevJson = resp.json
const prevJson = resp.json;
resp.json = function (obj) {
console.log('Response:', obj)
resp.json = function(obj) {
console.log("Response:", obj);
resp.json = prevJson
resp.json.apply(this, arguments)
}
resp.json = prevJson;
resp.json.apply(this, arguments);
};
next()
})
next();
});
app.post('/ChuniServlet/GetGameSettingApi', function (req, resp) {
app.post("/ChuniServlet/GetGameSettingApi", function(req, resp) {
resp.json({
gameSetting: {
dataVersion: 1,
isMaintenance: false,
requestInterval: 10,
rebootStartTime: 0,
rebootEndTime: 0,
isBackgroundDistribute: false,
maxCountCharacter: 999,
maxCountItem: 999,
maxCountMusic: 999,
},
isDumpUpload: false,
isAou: false
})
})
gameSetting: {
dataVersion: 1,
isMaintenance: false,
requestInterval: 10,
rebootStartTime: 0,
rebootEndTime: 0,
isBackgroundDistribute: false,
maxCountCharacter: 999,
maxCountItem: 999,
maxCountMusic: 999,
},
isDumpUpload: false,
isAou: false,
});
});
app.post('/ChuniServlet/UpsertClientSettingApi', function (req, resp) {
app.post("/ChuniServlet/UpsertClientSettingApi", function(req, resp) {
resp.json({
returnCode: 1,
apiName: "UpsertClientSettingApi"
})
})
returnCode: 1,
apiName: "UpsertClientSettingApi",
});
});
app.post('/ChuniServlet/UpsertClientBookkeepingApi', function (req, resp) {
app.post("/ChuniServlet/UpsertClientBookkeepingApi", function(req, resp) {
resp.json({
returnCode: 1,
apiName: "UpsertClientBookkeepingApi"
})
})
returnCode: 1,
apiName: "UpsertClientBookkeepingApi",
});
});
app.post('/ChuniServlet/UpsertClientTestmodeApi', function (req, resp) {
app.post("/ChuniServlet/UpsertClientTestmodeApi", function(req, resp) {
resp.json({
returnCode: 1,
apiName: "UpsertClientTestmodeApi"
})
})
returnCode: 1,
apiName: "UpsertClientTestmodeApi",
});
});
app.post('/ChuniServlet/UpsertClientErrorApi', function (req, resp) {
app.post("/ChuniServlet/UpsertClientErrorApi", function(req, resp) {
resp.json({
returnCode: 1,
apiName: "UpsertClientErrorApi"
})
})
returnCode: 1,
apiName: "UpsertClientErrorApi",
});
});
app.post('/ChuniServlet/UpsertClientDevelopApi', function (req, resp) {
app.post("/ChuniServlet/UpsertClientDevelopApi", function(req, resp) {
resp.json({
returnCode: 1,
apiName: "UpsertClientDevelopApi"
})
})
returnCode: 1,
apiName: "UpsertClientDevelopApi",
});
});
app.post('/ChuniServlet/GetGameMessageApi', function (req, resp) {
app.post("/ChuniServlet/GetGameMessageApi", function(req, resp) {
resp.json({
type: 1,
length: 0,
gameMessageList: [
/* {
type: 1,
length: 0,
gameMessageList: [
/* {
type: 2,
id: 1,
message: "true",
@@ -109,56 +109,61 @@ app.post('/ChuniServlet/GetGameMessageApi', function (req, resp) {
endDate: "0"
},*/
],
})
})
});
});
app.post('/ChuniServlet/GetGameIdlistApi', function (req, resp) {
const { type } = req.body
app.post("/ChuniServlet/GetGameIdlistApi", function(req, resp) {
const { type } = req.body;
resp.json({
type,
length: 0,
gameIdlistList: [],
})
})
});
});
app.post('/ChuniServlet/GetGameEventApi', function (req, resp) {
app.post("/ChuniServlet/GetGameEventApi", function(req, resp) {
resp.json({
type: 1,
length: 0,
gameEventList: [/*
gameEventList: [
/*
{
type: 1,
id: 1102, // data/A000/event/event00001102
startDate: 'STRINGIDK',
endDate: 'STRINGIDK',
},
*/],
})
})
*/
],
});
});
app.post('/ChuniServlet/GetGameRankingApi', function (req, resp) {
const { type } = req.body
app.post("/ChuniServlet/GetGameRankingApi", function(req, resp) {
const { type } = req.body;
resp.json({
type,
gameRankingList: [/*
gameRankingList: [
/*
// QWORD fields maybe?
{
id: 1,
point: 1,
}
*/],
})
})
*/
],
});
});
app.post('/ChuniServlet/GetGameSaleApi', function (req, resp) {
const { type } = req.body
app.post("/ChuniServlet/GetGameSaleApi", function(req, resp) {
const { type } = req.body;
resp.json({
type,
length: 0,
gameSaleList: [/*
gameSaleList: [
/*
{
orderId: 1234,
type,
@@ -167,14 +172,16 @@ app.post('/ChuniServlet/GetGameSaleApi', function (req, resp) {
startDate: 'STRINGIDK',
endDate: 'STRINGIDK',
},
*/],
})
})
*/
],
});
});
app.post('/ChuniServlet/GetGameChargeApi', function (req, resp) {
app.post("/ChuniServlet/GetGameChargeApi", function(req, resp) {
resp.json({
length: 0,
gameChargeList: [/*
gameChargeList: [
/*
{
orderId: 1,
chargeId: 1,
@@ -185,8 +192,9 @@ app.post('/ChuniServlet/GetGameChargeApi', function (req, resp) {
saleStartDate: 'STRINGIDK',
saleEndDate: 'STRINGIDK',
},
*/],
})
})
*/
],
});
});
module.exports = app
module.exports = app;

View File

@@ -1,19 +1,19 @@
const fs = require('fs')
const https = require('https')
const http = require('http')
const net = require('net')
const fs = require("fs");
const https = require("https");
const http = require("http");
const net = require("net");
const aimedb = require('./aimedb')
const billing = require('./billing')
const startup = require('./startup')
const chunithm = require('./chunithm')
const aimedb = require("./aimedb");
const billing = require("./billing");
const startup = require("./startup");
const chunithm = require("./chunithm");
const tls = {
cert: fs.readFileSync('pki/server.pem'),
key: fs.readFileSync('pki/server.key'),
}
cert: fs.readFileSync("pki/server.pem"),
key: fs.readFileSync("pki/server.key"),
};
net.createServer(aimedb).listen(22345)
http.createServer(startup).listen(80)
https.createServer(tls, billing).listen(8443)
http.createServer(chunithm).listen(9000)
net.createServer(aimedb).listen(22345);
http.createServer(startup).listen(80);
https.createServer(tls, billing).listen(8443);
http.createServer(chunithm).listen(9000);

View File

@@ -1,92 +1,92 @@
const bodyParser = require('body-parser')
const leftPad = require('left-pad') // the infamous...
const express = require('express')
const zlib = require('zlib')
const os = require('os')
const bodyParser = require("body-parser");
const leftPad = require("left-pad"); // the infamous...
const express = require("express");
const zlib = require("zlib");
const os = require("os");
const services = new Map();
services.set("SDBT", 9000); // Chunithm
function decodeRequest(req) {
const buf = Buffer.from(req, 'base64')
const bytes = zlib.unzipSync(buf)
const str = bytes.toString().trim()
const buf = Buffer.from(req, "base64");
const bytes = zlib.unzipSync(buf);
const str = bytes.toString().trim();
const kvps = str.split('&')
const params = {}
const kvps = str.split("&");
const params = {};
kvps.forEach(kvp => {
const [ key, val ] = kvp.split('=')
const [key, val] = kvp.split("=");
params[key] = val
})
params[key] = val;
});
return params
return params;
}
function encodeResponse(params) {
const str = Object.entries(
params
).map( ([ key, val ]) =>
key + '=' + val
).join('&') // Keys and values are not URL-escaped
const str = Object.entries(params)
.map(([key, val]) => key + "=" + val)
.join("&"); // Keys and values are not URL-escaped
return str + '\r\n'
return str + "\r\n";
}
const app = express()
const app = express();
// Startup request is url-encoded-ish... except it's also zlibed and base64ed.
// So in the absence of any exotic Transfer-Encoding headers this Content-Type
// is incorrect and we have to override Express' built-in handling.
app.use(bodyParser.raw({
type: 'application/x-www-form-urlencoded',
}))
app.use(
bodyParser.raw({
type: "application/x-www-form-urlencoded",
})
);
app.post('/sys/servlet/PowerOn', function (req, resp) {
const reqParams = decodeRequest(req.body.toString())
app.post("/sys/servlet/PowerOn", function(req, resp) {
const reqParams = decodeRequest(req.body.toString());
console.log('\n--- Startup Request ---\n\n', reqParams)
console.log("\n--- Startup Request ---\n\n", reqParams);
const now = new Date()
const now = new Date();
// Cut milliseconds out of ISO timestamp
const isoStrWithMs = now.toISOString()
const isoStr = isoStrWithMs.substr(0, 19) + 'Z'
const isoStrWithMs = now.toISOString();
const isoStr = isoStrWithMs.substr(0, 19) + "Z";
const respParams = {
stat: 1,
uri: `http://${os.hostname()}:${services.get(reqParams.game_id)}/`,
host: '',
place_id: '123',
name: 'Name',
nickname: 'Nick',
region0: '1',
region_name0: 'W',
region_name1: 'X',
region_name2: 'Y',
region_name3: 'Z',
country: 'JPN',
allnet_id: '456',
host: "",
place_id: "123",
name: "Name",
nickname: "Nick",
region0: "1",
region_name0: "W",
region_name1: "X",
region_name2: "Y",
region_name3: "Z",
country: "JPN",
allnet_id: "456",
// Always UTC to simplify the segatools fake time system
// (which is used to skip the hardcoded 2AM - 7AM warn/unplayable period)
client_timezone: '+0000',
client_timezone: "+0000",
utc_time: isoStr,
setting: '',
res_ver: '3',
setting: "",
res_ver: "3",
token: reqParams.token,
}
};
console.log('\n--- Startup Response ---\n\n', respParams)
console.log("\n--- Startup Response ---\n\n", respParams);
const respStrZ = encodeResponse(respParams)
const respStrZ = encodeResponse(respParams);
resp.send(respStrZ)
})
resp.send(respStrZ);
});
module.exports = app
module.exports = app;