|
|
|
|
@ -326,12 +326,12 @@ export class GameData extends Singleton { |
|
|
|
|
if (!s || s.length % 4 !== 0) return false; |
|
|
|
|
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(s)) return false; |
|
|
|
|
try { |
|
|
|
|
if (typeof atob !== 'function') return false; |
|
|
|
|
const bin = atob(s); |
|
|
|
|
const bin = this._base64Decode(s); |
|
|
|
|
if (!bin) return false; |
|
|
|
|
for (let i = 0; i < bin.length; i++) { |
|
|
|
|
const c = bin.charCodeAt(i); |
|
|
|
|
if (c < 32 && c !== 9 && c !== 10 && c !== 13) return false; |
|
|
|
|
if (c === 127 || c === 0xFFFD) return false; |
|
|
|
|
if (c === 127) return false; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} catch { |
|
|
|
|
@ -339,6 +339,28 @@ export class GameData extends Singleton { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Base64 → latin1 字符串(不依赖 atob,小游戏环境兜底);非法输入返回空串 */ |
|
|
|
|
private _base64Decode(b64: string): string { |
|
|
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; |
|
|
|
|
const lookup: Record<string, number> = {}; |
|
|
|
|
for (let i = 0; i < chars.length; i++) lookup[chars.charAt(i)] = i; |
|
|
|
|
const clean = b64.replace(/=+$/, ''); |
|
|
|
|
let out = ''; |
|
|
|
|
let buffer = 0; |
|
|
|
|
let bits = 0; |
|
|
|
|
for (let i = 0; i < clean.length; i++) { |
|
|
|
|
const v = lookup[clean.charAt(i)]; |
|
|
|
|
if (v === undefined) return ''; |
|
|
|
|
buffer = (buffer << 6) | v; |
|
|
|
|
bits += 6; |
|
|
|
|
if (bits >= 8) { |
|
|
|
|
bits -= 8; |
|
|
|
|
out += String.fromCharCode((buffer >> bits) & 255); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return out; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** UTF-8 → Base64(无 btoa 环境走手写实现;编码失败返回原文避免丢昵称) */ |
|
|
|
|
private _utf8ToBase64(s: string): string { |
|
|
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; |
|
|
|
|
|