pass sdvx requests to maomani

This commit is contained in:
GEEKiDoS
2025-04-14 16:13:17 +08:00
parent a47162655a
commit c8824eec5d
14 changed files with 635 additions and 3192 deletions
+61
View File
@@ -0,0 +1,61 @@
import { AcRelayInfo } from '../types.js';
import { fromKBinXml, toKBinXml, toObject } from './kbinxml.js';
import { Serializable } from './kxml-value.js';
import { LZ77 } from './lz77.js';
import { tokenToCardNumber } from './laochan-id.js';
import config from './config.js';
const PCB_ID = '1A0C1A0C1A0C1A0C1A0C';
export async function requestEa3(info: AcRelayInfo, model: string, token: string, ea3Url: string = 'http://maomani.cn:573/'): Promise<{
status: number,
response: Serializable,
}> {
if (config.isDev) {
console.log('ea3 call:');
}
const request = toKBinXml('call', {
[info.module]: {
$method: info.method,
$model: model,
$cardid: tokenToCardNumber(token),
$srcid: PCB_ID,
...info.request,
},
}, 'UTF-8', config.isDev);
const compressed = LZ77.compress(request.data);
const body = new Blob([compressed]);
const result = await fetch(`${ea3Url}/?model=${model}&f=${info.module}.${info.method}`, {
method: 'post',
body,
headers: {
'X-Compress': 'lz77',
},
}).then(async r => {
const raw = new Uint8Array(await r.arrayBuffer());
if (r.headers.get('x-compress') === 'lz77') {
return new Uint8Array(LZ77.decompress(raw));
}
return raw;
});
if (config.isDev) {
console.log('ea3 response:');
}
const response = fromKBinXml(result, config.isDev);
return {
status: parseInt(response['response'][info.module]?.$status ?? '0'),
response: response['response'],
};
}
export async function requestEa3Typed<T>(info: AcRelayInfo, model: string, token: string, ea3Url?: string): Promise<T> {
const { response } = await requestEa3(info, model, token, ea3Url);
return toObject<T>(response);
}
+29 -20
View File
@@ -1,4 +1,4 @@
import { to_bin, to_xml } from "@kamyu/kbinxml";
import { to_bin, to_xml } from "@geekidos/kbinxml";
import { XMLParser } from "fast-xml-parser";
import _ from "lodash";
import { Serializable } from "./kxml-value.js";
@@ -8,6 +8,7 @@ export const parser = new XMLParser({
parseAttributeValue: false,
attributeNamePrefix: '$',
removeNSPrefix: true,
textNodeName: '__value',
numberParseOptions: {
hex: false,
leadingZeros: false,
@@ -32,49 +33,49 @@ function parseValue(node: { $__type: string; $__count?: unknown }): any {
].includes(node.$__type)
) {
if (isArray) {
return node['#text'].split(' ').map((v) => parseInt(v));
return node['__value'].split(' ').map((v) => parseInt(v));
}
return parseInt(node['#text']);
return parseInt(node['__value']);
}
if (['float', 'double'].includes(node.$__type)) {
if (isArray) {
return node['#text'].split(' ').map((v) => parseFloat(v));
return node['__value'].split(' ').map((v) => parseFloat(v));
}
return parseFloat(node['#text']);
return parseFloat(node['__value']);
}
if (['b', 'bool'].includes(node.$__type)) {
return !!parseInt(node['#text']);
return !!parseInt(node['__value']);
}
if (['bin', 'binary'].includes(node.$__type)) {
return Buffer.from(node['#text'], 'hex');
return Buffer.from(node['__value'], 'hex');
}
if (['ip4', 'str', 'string'].includes(node.$__type)) {
return node['#text'];
return node['__value'];
}
if (node.$__type == 'time') {
return new Date(parseInt(node['#text']) * 1000);
return new Date(parseInt(node['__value']) * 1000);
}
return {
type: node.$__type,
value: node['#text'],
value: node['__value'],
};
}
export function toObject(node: Record<string, unknown> | unknown[] | string): any {
export function toObject<T = any>(node: Record<string, unknown> | unknown[] | string): T {
if (node instanceof Array) {
return node.map((v) => toObject(v as Record<string, unknown>));
return node.map((v) => toObject(v as Record<string, unknown>)) as T;
}
if (typeof node === 'string') {
return {};
return {} as T;
}
if ('$__type' in node) {
@@ -95,13 +96,17 @@ export function toObject(node: Record<string, unknown> | unknown[] | string): an
obj[key] = toObject(node[key] as Record<string, unknown>);
}
return obj;
return obj as T;
}
export function fromKBinXml(kbinxml: Uint8Array) {
const xml = to_xml(kbinxml).data;
const parsedXml = parser.parse(xml);
return toObject(parsedXml);
export function fromKBinXml(kbinxml: Uint8Array, dumpXml: boolean = false) {
const xml = to_xml(kbinxml, true).data;
if (dumpXml) {
console.log(xml);
}
return parser.parse(xml);
}
function serializeValue(value: any, type: string): string {
@@ -239,8 +244,12 @@ function serializeObject(obj: Serializable, name: string, linePrefix: string = '
return output + `</${name}>\n`;
}
export function toKBinXml(topName: string, obj: Serializable, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8') {
export function toKBinXml(topName: string, obj: Serializable, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8', dumpXml: boolean = false) {
const xml = `<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
console.log(xml);
if (dumpXml) {
console.log(xml);
}
return to_bin(xml);
}
+5 -58
View File
@@ -1,64 +1,11 @@
import {compress, decompress} from '@kamyu/lz77';
export class LZ77 {
static compress(input: ArrayLike<number>) {
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 compress(Uint8Array.from(input));
}
static decompress(a: ArrayLike<number>) {
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);
static decompress(input: ArrayLike<number>) {
return decompress(Uint8Array.from(input));
}
}