idz: Hoist userdb code

This commit is contained in:
Tau
2020-09-01 01:10:31 +00:00
committed by da5669c09fdb0a288ba01e259a609d7779ac7fc9
parent 6dceb7c48a
commit f959993197
245 changed files with 88 additions and 86 deletions
+15
View File
@@ -0,0 +1,15 @@
export function bitmap<T extends number>(buf: Buffer): Set<T> {
const result = new Set<T>();
for (let byteNo = 0; byteNo < buf.length; byteNo++) {
const byte = buf.readUInt8(byteNo);
for (let bitNo = 0; bitNo < 8; bitNo++) {
if (byte & (1 << bitNo)) {
result.add((8 * byteNo + bitNo) as T);
}
}
}
return result;
}
+27
View File
@@ -0,0 +1,27 @@
import { Car, CarSelector } from "../model/car";
export function car(buf: Buffer): Car {
const field_04: number[] = [];
for (let i = 0; i < 32; i++) {
field_04.push(buf.readUInt16LE(0x0004 + 2 * i));
}
return {
field_00: buf.readUInt16LE(0x0000),
field_02: buf.readUInt16LE(0x0002),
field_04,
selector: buf.readUInt16LE(0x0044) as CarSelector,
field_46: buf.readUInt16LE(0x0046),
field_48: buf.readUInt16LE(0x0048),
field_4a: buf.readUInt16LE(0x004a),
field_4c: buf.readUInt32LE(0x004c),
field_50_lo: buf.readUInt32LE(0x0050),
field_50_hi: buf.readUInt32LE(0x0054),
field_58: buf.readUInt16LE(0x0058),
field_5a: buf.readUInt8(0x005a),
field_5b: buf.readUInt8(0x005b),
field_5c: buf.readUInt16LE(0x005c),
field_5e: buf.readUInt16LE(0x005e),
};
}
+17
View File
@@ -0,0 +1,17 @@
import { BackgroundCode, TitleCode } from "../model/base";
import { Chara } from "../model/chara";
export function chara(buf: Buffer): Chara {
return {
gender: buf.readUInt16LE(0x00) === 0 ? "male" : "female",
field_02: buf.readUInt16LE(0x02),
field_04: buf.readUInt16LE(0x04),
field_06: buf.readUInt16LE(0x06),
field_08: buf.readUInt16LE(0x08),
field_0a: buf.readUInt16LE(0x0a),
field_0c: buf.readUInt16LE(0x0c),
field_0e: buf.readUInt16LE(0x0e),
title: buf.readUInt16LE(0x10) as TitleCode, // Swapped on load
background: buf.readUInt16LE(0x12) as BackgroundCode, // Swapped on load
};
}
+28
View File
@@ -0,0 +1,28 @@
import { MissionGrid } from "../model/mission";
export function mission(buf: Buffer): MissionGrid[] {
const grids = new Array();
for (let gridNo = 0; gridNo < 5; gridNo++) {
const grid: MissionGrid = { cells: [] };
for (let cellNo = 0; cellNo < 9; cellNo++) {
let cell = 0;
let mask = 1 << cellNo;
for (let sliceNo = 0; sliceNo < 3; sliceNo++) {
const slice = buf.readUInt16LE(0x0c * sliceNo + 0x02 * gridNo);
if (slice & mask) {
cell |= 1 << sliceNo;
}
}
grid.cells.push(cell);
}
grids.push(grid);
}
return grids;
}
+8
View File
@@ -0,0 +1,8 @@
import { CheckTeamNameRequest } from "../request/checkTeamName";
checkTeamName.msgCode = 0x00a2;
checkTeamName.msgLen = 0x0040;
export function checkTeamName(buf: Buffer): CheckTeamNameRequest {
return { type: "check_team_name_req" };
}
+14
View File
@@ -0,0 +1,14 @@
import { CreateAutoTeamRequest } from "../request/createAutoTeam";
import { AimeId } from "../../../model";
createAutoTeam.msgCode = 0x007b;
createAutoTeam.msgLen = 0x0010;
export function createAutoTeam(buf: Buffer): CreateAutoTeamRequest {
return {
type: "create_auto_team_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
field_0008: buf.readUInt32LE(0x0008),
field_000C: buf.readUInt8(0x000c),
};
}
+24
View File
@@ -0,0 +1,24 @@
import iconv from "iconv-lite";
import { car } from "./_car";
import { chara } from "./_chara";
import { CreateProfileRequest } from "../request/createProfile";
import { AimeId } from "../../../model";
createProfile.msgCode = 0x0066;
createProfile.msgLen = 0x00c0;
export function createProfile(buf: Buffer): CreateProfileRequest {
return {
type: "create_profile_req",
aimeId: buf.readInt32LE(0x0004) as AimeId,
luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"),
name: iconv.decode(
buf.slice(0x001e, buf.indexOf("\0", 0x001e)),
"shift_jis"
),
field_0034: buf.readUInt32LE(0x0034),
car: car(buf.slice(0x0040, 0x00a0)),
chara: chara(buf.slice(0x00a0, 0x00b4)),
};
}
+24
View File
@@ -0,0 +1,24 @@
import iconv from "iconv-lite";
import { CreateTeamRequest } from "../request/createTeam";
import { AimeId } from "../../../model";
createTeam.msgCode = 0x0071;
createTeam.msgLen = 0x0050;
export function createTeam(buf: Buffer): CreateTeamRequest {
return {
type: "create_team_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
teamName: iconv.decode(
buf.slice(0x0008, buf.indexOf("\0", 0x0008)),
"shift_jis"
),
field_0028: buf.readUInt16LE(0x0028),
field_002C: buf.readUInt32LE(0x002c),
nameBg: buf.readUInt8(0x0030),
field_0032: buf.readUInt16LE(0x0032),
prevTeamId: buf.readUInt32LE(0x0034),
pcbId: buf.slice(0x0038, buf.indexOf("\0", 0x0038)).toString("ascii"),
};
}
+12
View File
@@ -0,0 +1,12 @@
import { DiscoverProfileRequest } from "../request/discoverProfile";
import { AimeId } from "../../../model";
discoverProfile.msgCode = 0x006b;
discoverProfile.msgLen = 0x0010;
export function discoverProfile(buf: Buffer): DiscoverProfileRequest {
return {
type: "discover_profile_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
};
}
+206
View File
@@ -0,0 +1,206 @@
import logger from "debug";
import { Transform } from "stream";
import { checkTeamName } from "./checkTeamName";
import { createProfile } from "./createProfile";
import { createTeam } from "./createTeam";
import { createAutoTeam } from "./createAutoTeam";
import { discoverProfile } from "./discoverProfile";
import { load2on2_v1, load2on2_v2 } from "./load2on2";
import { loadConfig } from "./loadConfig";
import { loadConfig2 } from "./loadConfig2";
import { loadEventInfo } from "./loadEventInfo";
import { loadGacha } from "./loadGacha";
import { loadGarage } from "./loadGarage";
import { loadGeneralReward1, loadGeneralReward2 } from "./loadGeneralReward";
import { loadGhost } from "./loadGhost";
import { loadProfile2, loadProfile3 } from "./loadProfile";
import { loadRewardTable } from "./loadRewardTable";
import { loadServerList } from "./loadServerList";
import { loadStocker } from "./loadStocker";
import { loadTeam } from "./loadTeam";
import { loadTeamRanking, loadTeamRanking2 } from "./loadTeamRanking";
import { loadTopTen1 } from "./loadTopTen1";
import { loadTopTen2 } from "./loadTopTen2";
import { lockGarage } from "./lockGarage";
import { lockProfile } from "./lockProfile";
import { msg00AD } from "./msg00AD";
import { saveExpedition1, saveExpedition2 } from "./saveExpedition";
import { saveGarage } from "./saveGarage";
import { saveNewCar } from "./saveNewCar";
import { saveProfile2 } from "./saveProfile2";
import { saveProfile3 } from "./saveProfile3";
import { saveSettings } from "./saveSettings";
import { saveStocker } from "./saveStocker";
import { saveTeamBanner } from "./saveTeamBanner";
import { saveTimeAttack1, saveTimeAttack2 } from "./saveTimeAttack";
import { saveTopic } from "./saveTopic";
import { unlockProfile } from "./unlockProfile";
import { updateProvisionalStoreRank } from "./updateProvisionalStoreRank";
import { updateTeamLeader } from "./updateTeamLeader";
import { updateTeamMember } from "./updateTeamMember";
import {
updateStoryClearNum1,
updateStoryClearNum2,
} from "./updateStoryClearNum";
import { Request } from "../request";
import { updateResult } from "./updateResult";
import { updateTeamPoints } from "./updateTeamPoints";
import { updateUiReport } from "./updateUiReport";
import { updateUserLog } from "./updateUserLog";
import { lockProfileExtend } from "./lockProfileExtend";
const debug = logger("app:idz:userdb:decoder");
export type ReaderFn = ((buf: Buffer) => Request) & {
msgCode: number;
msgLen: number;
};
const funcList: ReaderFn[] = [
checkTeamName,
createAutoTeam,
createProfile,
createTeam,
discoverProfile,
load2on2_v1,
load2on2_v2,
loadConfig,
loadConfig2,
loadEventInfo,
loadGacha,
loadGarage,
loadGeneralReward1,
loadGeneralReward2,
loadGhost,
loadProfile2,
loadProfile3,
loadRewardTable,
loadServerList,
loadStocker,
loadTeam,
loadTeamRanking,
loadTeamRanking2,
loadTopTen1,
loadTopTen2,
lockGarage,
lockProfile,
lockProfileExtend,
msg00AD,
saveExpedition1,
saveExpedition2,
saveGarage,
saveNewCar,
saveProfile2,
saveProfile3,
saveSettings,
saveStocker,
saveTeamBanner,
saveTimeAttack1,
saveTimeAttack2,
saveTopic,
unlockProfile,
updateProvisionalStoreRank,
updateResult,
updateStoryClearNum1,
updateStoryClearNum2,
updateTeamLeader,
updateTeamMember,
updateTeamPoints,
updateUiReport,
updateUserLog,
];
const readerFns = new Map<number, ReaderFn>();
const msgLengths = new Map<number, number>();
for (const fn of funcList) {
readerFns.set(fn.msgCode, fn);
msgLengths.set(fn.msgCode, fn.msgLen);
}
function readHeader(buf: Buffer) {
return {
blah: "blah",
};
}
export class Decoder extends Transform {
state: Buffer;
constructor() {
super({
readableObjectMode: true,
writableObjectMode: true,
});
this.state = Buffer.alloc(0);
}
_transform(chunk: Buffer, encoding, callback) {
this.state = Buffer.concat([this.state, chunk]);
// Read header
if (this.state.length < 0x04) {
return callback(null);
}
const magic = this.state.readUInt32LE(0);
if (magic !== 0x01020304) {
return callback(
new Error(
"Invalid magic number, cryptographic processing probably incorrect."
)
);
}
if (this.state.length < 0x30) {
return callback(null);
}
const header = readHeader(this.state);
if (this.state.length < 0x32) {
return callback(null);
}
const msgCode = this.state.readUInt16LE(0x30);
const msgLen = msgLengths.get(msgCode);
if (msgLen === undefined) {
return callback(
new Error(
`Unknown command code ${msgCode.toString(16)}, cannot continue`
)
);
}
if (this.state.length < 0x30 + msgLen) {
return callback(null);
}
const reqBuf = this.state.slice(0, 0x30 + msgLen);
const payloadBuf = reqBuf.slice(0x30);
if (debug.enabled) {
debug("Raw: %s", reqBuf.toString("hex"));
debug("Header: %j", header);
}
const reader = readerFns.get(msgCode);
if (reader === undefined) {
return callback(
new Error(`No reader for command code ${msgCode.toString(16)}`)
);
}
const payload = reader(payloadBuf);
debug("Payload: %j", payload);
return callback(null, payload);
}
}
+30
View File
@@ -0,0 +1,30 @@
import { ExtId } from "../model/base";
import { Team } from "../model/team";
import { Load2on2Request1, Load2on2Request2 } from "../request/load2on2";
import { AimeId } from "../../../model";
load2on2_v1.msgCode = 0x00b0;
load2on2_v1.msgLen = 0x0010;
export function load2on2_v1(buf: Buffer): Load2on2Request1 {
return {
type: "load_2on2_req",
format: 1,
field_0002: buf.readUInt16LE(0x0002),
aimeId: buf.readUInt32LE(0x0004) as AimeId,
teamId: buf.readUInt32LE(0x0008) as ExtId<Team>,
};
}
load2on2_v2.msgCode = 0x0132;
load2on2_v2.msgLen = 0x0010;
export function load2on2_v2(buf: Buffer): Load2on2Request2 {
return {
type: "load_2on2_req",
format: 2,
field_0002: buf.readUInt16LE(0x0002),
aimeId: buf.readUInt32LE(0x0004) as AimeId,
teamId: buf.readUInt32LE(0x0008) as ExtId<Team>,
};
}
+8
View File
@@ -0,0 +1,8 @@
import { LoadConfigRequest } from "../request/loadConfig";
loadConfig.msgCode = 0x0004;
loadConfig.msgLen = 0x0050;
export function loadConfig(): LoadConfigRequest {
return { type: "load_config_req" };
}
+8
View File
@@ -0,0 +1,8 @@
import { LoadConfigRequest2 } from "../request/loadConfig2";
loadConfig2.msgCode = 0x00ab;
loadConfig2.msgLen = 0x0010;
export function loadConfig2(): LoadConfigRequest2 {
return { type: "load_config_v2_req" };
}
+12
View File
@@ -0,0 +1,12 @@
import { LoadEventInfoRequest } from "../request/loadEventInfo";
import { AimeId } from "../../../model";
loadEventInfo.msgCode = 0x00be;
loadEventInfo.msgLen = 0x0010;
export function loadEventInfo(buf: Buffer): LoadEventInfoRequest {
return {
type: "load_event_info_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
};
}
+12
View File
@@ -0,0 +1,12 @@
import { LoadGachaRequest } from "../request/loadGacha";
import { AimeId } from "../../../model";
loadGacha.msgCode = 0x00c1;
loadGacha.msgLen = 0x0010;
export function loadGacha(buf: Buffer): LoadGachaRequest {
return {
type: "load_gacha_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
};
}
+14
View File
@@ -0,0 +1,14 @@
import { LoadGarageRequest } from "../request/loadGarage";
import { AimeId } from "../../../model";
loadGarage.msgCode = 0x0090;
loadGarage.msgLen = 0x0010;
export function loadGarage(buf: Buffer): LoadGarageRequest {
return {
type: "load_garage_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
fetchOffset: buf.readUInt8(0x0008),
field_000A: buf.readUInt16LE(0x000a),
};
}
@@ -0,0 +1,27 @@
import {
LoadGeneralRewardRequest1,
LoadGeneralRewardRequest2,
} from "../request/loadGeneralReward";
import { AimeId } from "../../../model";
loadGeneralReward1.msgCode = 0x009c;
loadGeneralReward1.msgLen = 0x0010;
export function loadGeneralReward1(buf: Buffer): LoadGeneralRewardRequest1 {
return {
type: "load_general_reward_req",
format: 1,
aimeId: buf.readUInt32LE(0x0004) as AimeId,
};
}
loadGeneralReward2.msgCode = 0x013b;
loadGeneralReward2.msgLen = 0x0010;
export function loadGeneralReward2(buf: Buffer): LoadGeneralRewardRequest2 {
return {
type: "load_general_reward_req",
format: 2,
aimeId: buf.readUInt32LE(0x0004) as AimeId,
};
}
+15
View File
@@ -0,0 +1,15 @@
import { LoadGhostRequest } from "../request/loadGhost";
loadGhost.msgCode = 0x00a0;
loadGhost.msgLen = 0x0010;
export function loadGhost(buf: Buffer): LoadGhostRequest {
return {
type: "load_ghost_req",
field_0002: buf.readUInt16LE(0x0002),
field_0004: buf.readUInt16LE(0x0004),
field_0008: buf.readUInt32LE(0x0008),
field_000C: buf.readUInt16LE(0x000c),
field_000E: buf.readUInt16LE(0x000e),
};
}
+29
View File
@@ -0,0 +1,29 @@
import {
LoadProfileRequest2,
LoadProfileRequest3,
} from "../request/loadProfile";
import { AimeId } from "../../../model";
loadProfile2.msgCode = 0x0067;
loadProfile2.msgLen = 0x0020;
export function loadProfile2(buf: Buffer): LoadProfileRequest2 {
return {
type: "load_profile_req",
format: 2,
aimeId: buf.readUInt32LE(0x0004) as AimeId,
luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"),
};
}
loadProfile3.msgCode = 0x0012f;
loadProfile3.msgLen = 0x0020;
export function loadProfile3(buf: Buffer): LoadProfileRequest3 {
return {
type: "load_profile_req",
format: 3,
aimeId: buf.readUInt32LE(0x0004) as AimeId,
luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"),
};
}
+10
View File
@@ -0,0 +1,10 @@
import { LoadRewardTableRequest } from "../request/loadRewardTable";
loadRewardTable.msgCode = 0x0086;
loadRewardTable.msgLen = 0x0010;
export function loadRewardTable(): LoadRewardTableRequest {
return {
type: "load_reward_table_req",
};
}
+8
View File
@@ -0,0 +1,8 @@
import { LoadServerListRequest } from "../request/loadServerList";
loadServerList.msgCode = 0x0006;
loadServerList.msgLen = 0x0020;
export function loadServerList(): LoadServerListRequest {
return { type: "load_server_list_req" };
}
+12
View File
@@ -0,0 +1,12 @@
import { LoadStockerRequest } from "../request/loadStocker";
import { AimeId } from "../../../model";
loadStocker.msgCode = 0x00a7;
loadStocker.msgLen = 0x0010;
export function loadStocker(buf: Buffer): LoadStockerRequest {
return {
type: "load_stocker_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
};
}
+16
View File
@@ -0,0 +1,16 @@
import { LoadTeamRequest } from "../request/loadTeam";
import { ExtId } from "../model/base";
import { Team } from "../model/team";
loadTeam.msgCode = 0x0077;
loadTeam.msgLen = 0x0010;
export function loadTeam(buf: Buffer): LoadTeamRequest {
const extId = buf.readUInt32LE(0x0008);
return {
type: "load_team_req",
aimeId: buf.readUInt32LE(0x0004),
teamExtId: extId !== 0xffffffff ? (extId as ExtId<Team>) : undefined,
};
}
+21
View File
@@ -0,0 +1,21 @@
import { LoadTeamRankingRequest } from "../request/loadTeamRanking";
loadTeamRanking.msgCode = 0x00b9;
loadTeamRanking.msgLen = 0x0010;
export function loadTeamRanking(buf: Buffer): LoadTeamRankingRequest {
return {
type: "load_team_ranking_req",
};
}
// not sure what the difference is...
loadTeamRanking2.msgCode = 0x00bb;
loadTeamRanking2.msgLen = 0x0010;
export function loadTeamRanking2(buf: Buffer): LoadTeamRankingRequest {
return {
type: "load_team_ranking_req",
};
}
+35
View File
@@ -0,0 +1,35 @@
import { ExtId, RouteNo } from "../model/base";
import { Team } from "../model/team";
import {
LoadTopTenRequest,
LoadTopTenRequestSelector,
} from "../request/loadTopTen";
import { AimeId } from "../../../model";
loadTopTen1.msgCode = 0x00b5;
loadTopTen1.msgLen = 0x00e0;
export function loadTopTen1(buf: Buffer): LoadTopTenRequest {
const selectors = new Array<LoadTopTenRequestSelector>();
for (let i = 0; i < 32; i++) {
selectors.push({
routeNo: (buf.readUInt16LE(0x0004 + 2 * i) >> 1) as RouteNo,
minTimestamp: new Date(buf.readUInt32LE(0x0044 + 4 * i) * 1000 + 1000),
});
}
const profileId = buf.readUInt32LE(0x00c8);
const teamId = buf.readUInt32LE(0x00cc);
return {
type: "load_top_ten_req",
field_2: buf.readUInt16LE(0x0002), // Bitmask selector
selectors,
field_C4: buf.readUInt8(0x00c4), // Boolean, true if profile ID is set
field_C5: buf.readUInt8(0x00c5), // Always zero
field_C6: buf.readUInt16LE(0x00c6),
aimeId: profileId !== 0 ? (profileId as AimeId) : undefined,
teamId: teamId !== 0xffffffff ? (teamId as ExtId<Team>) : undefined,
};
}
+35
View File
@@ -0,0 +1,35 @@
import { ExtId, RouteNo } from "../model/base";
import { Team } from "../model/team";
import {
LoadTopTenRequest,
LoadTopTenRequestSelector,
} from "../request/loadTopTen";
import { AimeId } from "../../../model";
loadTopTen2.msgCode = 0x012c;
loadTopTen2.msgLen = 0x0110;
export function loadTopTen2(buf: Buffer): LoadTopTenRequest {
const selectors = new Array<LoadTopTenRequestSelector>();
for (let i = 0; i < 40; i++) {
selectors.push({
routeNo: (buf.readUInt16LE(0x0004 + 2 * i) >> 1) as RouteNo,
minTimestamp: new Date(buf.readUInt32LE(0x0054 + 4 * i) * 1000 + 1000),
});
}
const profileId = buf.readUInt32LE(0x00f8);
const teamId = buf.readUInt32LE(0x00fc);
return {
type: "load_top_ten_req",
field_2: buf.readUInt16LE(0x0002), // Bitmask selector
selectors,
field_C4: buf.readUInt8(0x00f4), // Boolean, true if profile ID is set
field_C5: buf.readUInt8(0x00f5), // Always zero
field_C6: buf.readUInt16LE(0x00f6),
aimeId: profileId !== 0 ? (profileId as AimeId) : undefined,
teamId: teamId !== 0xffffffff ? (teamId as ExtId<Team>) : undefined,
};
}
+11
View File
@@ -0,0 +1,11 @@
import { LockGarageRequest } from "../request/lockGarage";
lockGarage.msgCode = 0x00a9;
lockGarage.msgLen = 0x0010;
export function lockGarage(buf: Buffer): LockGarageRequest {
return {
type: "lock_garage_request",
field_0004: buf.readUInt32LE(0x0004),
};
}
+13
View File
@@ -0,0 +1,13 @@
import { LockProfileRequest } from "../request/lockProfile";
lockProfile.msgCode = 0x0069;
lockProfile.msgLen = 0x0020;
export function lockProfile(buf: Buffer): LockProfileRequest {
return {
type: "lock_profile_req",
aimeId: buf.readUInt32LE(0x0004),
pcbId: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"),
field_0018: buf.readUInt16LE(0x0018),
};
}
@@ -0,0 +1,13 @@
import { LockProfileExtendRequest } from "../request/lockProfileExtend";
import { AimeId } from "../../../model";
lockProfileExtend.msgCode = 0x006d;
lockProfileExtend.msgLen = 0x0020;
export function lockProfileExtend(buf: Buffer): LockProfileExtendRequest {
return {
type: "lock_profile_extend_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
luid: buf.slice(0x0008, buf.indexOf("\0")).toString("ascii"),
};
}
+8
View File
@@ -0,0 +1,8 @@
import { Message00AD } from "../request/msg00AD";
msg00AD.msgCode = 0x00ad;
msg00AD.msgLen = 0x061c;
export function msg00AD(buf: Buffer): Message00AD {
return { type: "msg_00AD_req" };
}
+26
View File
@@ -0,0 +1,26 @@
import {
SaveExpeditionRequest1,
SaveExpeditionRequest2,
} from "../request/saveExpedition";
saveExpedition1.msgCode = 0x008c;
saveExpedition1.msgLen = 0x0010;
export function saveExpedition1(buf: Buffer): SaveExpeditionRequest1 {
return {
type: "save_expedition_req",
format: 1,
field_0004: buf.readUInt32LE(0x0004),
};
}
saveExpedition2.msgCode = 0x013f;
saveExpedition2.msgLen = 0x0010;
export function saveExpedition2(buf: Buffer): SaveExpeditionRequest2 {
return {
type: "save_expedition_req",
format: 2,
field_0004: buf.readUInt32LE(0x0004),
};
}
+22
View File
@@ -0,0 +1,22 @@
import { car } from "./_car";
import { SaveGarageRequest } from "../request/saveGarage";
saveGarage.msgCode = 0x008e;
saveGarage.msgLen = 0x0090;
export function saveGarage(buf: Buffer): SaveGarageRequest {
const field_0068: number[] = [];
for (let offset = 0x0068; offset < 0x007c; offset += 2) {
field_0068.push(buf.readUInt16LE(offset));
}
return {
type: "save_garage_req",
aimeId: buf.readUInt32LE(0x0004),
payload: car(buf.slice(0x0008, 0x0068)),
field_0068,
field_0080: buf.readUInt8(0x0080),
field_0081: buf.readUInt8(0x0081) !== 0,
};
}
+16
View File
@@ -0,0 +1,16 @@
import { car } from "./_car";
import { SaveNewCarRequest } from "../request/saveNewCar";
import { AimeId } from "../../../model";
saveNewCar.msgCode = 0x0079;
saveNewCar.msgLen = 0x0090;
export function saveNewCar(buf: Buffer): SaveNewCarRequest {
return {
type: "save_new_car_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
luid: buf.slice(0x0008, buf.indexOf(0, 0x0008)).toString("ascii"),
car: car(buf.slice(0x0020, 0x0080)),
field_0080: buf.readUInt32LE(0x0080),
};
}
+99
View File
@@ -0,0 +1,99 @@
import { car } from "./_car";
import { mission } from "./_mission";
import { BackgroundCode, CourseNo, TitleCode } from "../model/base";
import { SaveProfileRequest2 } from "../request/saveProfile";
import { bitmap } from "./_bitmap";
import { AimeId } from "../../../model";
saveProfile2.msgCode = 0x0068;
saveProfile2.msgLen = 0x0940;
export function saveProfile2(buf: Buffer): SaveProfileRequest2 {
const storyRows = new Array();
for (let i = 0; i < 9; i++) {
const cells = new Array();
const rowOffset = 0x01a8 + i * 0x3c;
for (let j = 0; j < 9; j++) {
const a = buf.readUInt32LE(rowOffset + 0x04 + j * 4);
const b = buf.readUInt16LE(rowOffset + 0x28 + j * 2);
const cell = { a, b };
cells.push(cell);
}
const row = { cells };
storyRows.push(row);
}
const coursePlays = new Map<CourseNo, number>();
for (let i = 0; i < 16; i++) {
coursePlays.set(i as CourseNo, buf.readUInt16LE(0x04c0 + 2 * i));
}
const freeCar = {
validFrom: buf.readUInt32LE(0x0138),
};
const freeContinue = {
validFrom: buf.readUInt32LE(0x0038),
validTo: buf.readUInt32LE(0x003c),
};
return {
type: "save_profile_req",
format: 2,
aimeId: buf.readUInt32LE(0x0004) as AimeId,
lv: buf.readUInt16LE(0x0026),
exp: buf.readUInt32LE(0x0028),
fame: buf.readUInt32LE(0x0468),
dpoint: buf.readUInt32LE(0x0464),
mileage: buf.readUInt32LE(0x0008),
title: buf.readUInt16LE(0x0040) as TitleCode,
titles: bitmap(buf.slice(0x0042, 0x00f6)),
background: buf.readUInt8(0x0750) as BackgroundCode,
coursePlays,
missions: {
team: mission(buf.slice(0x03c4, 0x03e6)),
solo: mission(buf.slice(0x0724, 0x0746)),
},
car: car(buf.slice(0x0834, 0x0894)),
story: {
x: buf.readUInt16LE(0x06fc),
y: buf.readUInt8(0x06e0),
rows: storyRows,
},
unlocks: {
auras: buf.readUInt16LE(0x010c),
cup: buf.readUInt8(0x0110),
gauges: buf.readUInt16LE(0x0114),
music: buf.readUInt16LE(0x0140),
lastMileageReward: buf.readUInt32LE(0x013c),
},
tickets: {
freeCar:
freeCar.validFrom !== 0
? {
validFrom: new Date(freeCar.validFrom * 1000),
}
: undefined,
freeContinue:
freeContinue.validFrom !== 0 && freeContinue.validTo !== 0
? {
validFrom: new Date(freeContinue.validFrom * 1000),
validTo: new Date(freeContinue.validTo * 1000),
}
: undefined,
},
settings: {
music: buf.readUInt16LE(0x045a),
pack: buf.readUInt32LE(0x0034),
aura: buf.readUInt8(0x002c),
paperCup: buf.readUInt8(0x00f6),
gauges: buf.readUInt8(0x00f7),
},
};
}
+101
View File
@@ -0,0 +1,101 @@
import { car } from "./_car";
import { mission } from "./_mission";
import { BackgroundCode, CourseNo, TitleCode } from "../model/base";
import { SaveProfileRequest2 } from "../request/saveProfile";
import { bitmap } from "./_bitmap";
import { AimeId } from "../../../model";
saveProfile3.msgCode = 0x0138;
saveProfile3.msgLen = 0x0a70;
export function saveProfile3(buf: Buffer): SaveProfileRequest2 {
const storyRows = new Array();
// Story layout has changed somewhat...
for (let i = 0; i < 27; i++) {
const cells = new Array();
const rowOffset = 0x01ac + i * 0x18;
for (let j = 0; j < 9; j++) {
const a = buf.readUInt8(rowOffset + 0x00 + j);
const b = buf.readUInt8(rowOffset + 0x09 + j);
const cell = { a, b };
cells.push(cell);
}
const row = { cells };
storyRows.push(row);
}
const coursePlays = new Map<CourseNo, number>();
for (let i = 0; i < 20; i++) {
coursePlays.set(i as CourseNo, buf.readUInt16LE(0x0554 + 2 * i));
}
const freeCar = {
validFrom: buf.readUInt32LE(0x0138),
};
const freeContinue = {
validFrom: buf.readUInt32LE(0x0038),
validTo: buf.readUInt32LE(0x003c),
};
return {
type: "save_profile_req",
format: 2,
aimeId: buf.readUInt32LE(0x0004) as AimeId,
lv: buf.readUInt16LE(0x0026),
exp: buf.readUInt32LE(0x0028),
fame: buf.readUInt32LE(0x04fc),
dpoint: buf.readUInt32LE(0x04f8),
mileage: buf.readUInt32LE(0x0008),
title: buf.readUInt16LE(0x0040) as TitleCode,
titles: bitmap(buf.slice(0x0042, 0x00f6)),
background: buf.readUInt8(0x0874) as BackgroundCode,
coursePlays,
missions: {
team: mission(buf.slice(0x0430, 0x0452)),
solo: mission(buf.slice(0x0848, 0x086a)),
},
car: car(buf.slice(0x0958, 0x09b8)),
story: {
x: buf.readUInt16LE(0x0818),
y: buf.readUInt8(0x07fc),
rows: storyRows,
},
unlocks: {
auras: buf.readUInt16LE(0x010c),
cup: buf.readUInt8(0x0110),
gauges: buf.readUInt16LE(0x0114),
music: buf.readUInt16LE(0x0140),
lastMileageReward: buf.readUInt32LE(0x013c),
},
tickets: {
freeCar:
freeCar.validFrom !== 0
? {
validFrom: new Date(freeCar.validFrom * 1000),
}
: undefined,
freeContinue:
freeContinue.validFrom !== 0 && freeContinue.validTo !== 0
? {
validFrom: new Date(freeContinue.validFrom * 1000),
validTo: new Date(freeContinue.validTo * 1000),
}
: undefined,
},
settings: {
music: buf.readUInt16LE(0x04ee),
pack: buf.readUInt32LE(0x0034),
aura: buf.readUInt8(0x002c),
paperCup: buf.readUInt8(0x00f6),
gauges: buf.readUInt8(0x00f7),
},
};
}
+23
View File
@@ -0,0 +1,23 @@
import { SaveSettingsRequest } from "../request/saveSettings";
import { AimeId } from "../../../model";
saveSettings.msgCode = 0x00a5;
saveSettings.msgLen = 0x0020;
export function saveSettings(buf: Buffer): SaveSettingsRequest {
const pack = buf.readUInt32LE(0x000c);
return {
type: "save_settings_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
dpoint: buf.readUInt32LE(0x0008),
settings: {
music: buf.readUInt16LE(0x0002),
pack: buf.readUInt32LE(0x000c),
paperCup: buf.readUInt8(0x0011),
gauges: buf.readUInt8(0x0012),
aura: buf.readUInt8(0x0013),
},
field_0010: buf.readUInt8(0x0010),
};
}
+19
View File
@@ -0,0 +1,19 @@
import { bitmap } from "./_bitmap";
import { chara } from "./_chara";
import { CarSelector } from "../model/car";
import { SaveStockerRequest } from "../request/saveStocker";
import { AimeId } from "../../../model";
saveStocker.msgCode = 0x00a6;
saveStocker.msgLen = 0x00c0;
export function saveStocker(buf: Buffer): SaveStockerRequest {
return {
type: "save_stocker_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
backgrounds: bitmap(buf.slice(0x0008, 0x002c)),
selectedCar: buf.readUInt16LE(0x009c) as CarSelector,
chara: chara(buf.slice(0x009e, 0x00b2)),
};
}
+15
View File
@@ -0,0 +1,15 @@
import { SaveTeamBannerRequest } from "../request/saveTeamBanner";
import { ExtId } from "../model/base";
import { Team } from "../model/team";
saveTeamBanner.msgCode = 0x0089;
saveTeamBanner.msgLen = 0x0010;
export function saveTeamBanner(buf: Buffer): SaveTeamBannerRequest {
return {
type: "save_team_banner_req",
teamExtId: buf.readUInt32LE(0x0004) as ExtId<Team>,
nameBg: buf.readUInt32LE(0x0008),
nameFx: buf.readUInt32LE(0x000c),
};
}
+50
View File
@@ -0,0 +1,50 @@
import { RouteNo } from "../model/base";
import { CarSelector } from "../model/car";
import { SaveTimeAttackRequest } from "../request/saveTimeAttack";
import { AimeId } from "../../../model";
function saveTimeAttack(buf: Buffer): SaveTimeAttackRequest {
return {
type: "save_time_attack_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
dayNight: buf.readUInt8(0x0054) & 1,
payload: {
routeNo: (buf.readUInt8(0x0054) >> 1) as RouteNo,
timestamp: new Date(buf.readUInt32LE(0x0058) * 1000),
flags: buf.readUInt8(0x005c),
totalTime: buf.readUInt32LE(0x0018) / 1000,
sectionTimes: [
buf.readUInt32LE(0x0024) / 1000,
buf.readUInt32LE(0x0028) / 1000,
buf.readUInt32LE(0x002c) / 1000,
],
grade: buf.readUInt8(0x0062),
carSelector: buf.readUInt16LE(0x000c) as CarSelector,
},
field_0002: buf.readUInt16LE(0x0002),
field_0008: buf.readUInt32LE(0x0008),
field_0012: buf.readUInt8(0x0012),
field_0015: buf.readUInt8(0x0015),
field_005D: buf.readUInt8(0x005d),
field_005E: buf.readUInt16LE(0x005e),
field_0060: buf.readUInt16LE(0x0060),
};
}
// There is ... literally no difference between these messages other than their
// request code..? Even the response uses the same response code, despite
// the request codes differing.
saveTimeAttack1.msgCode = 0x00cd;
saveTimeAttack1.msgLen = 0x0080;
export function saveTimeAttack1(buf: Buffer): SaveTimeAttackRequest {
return saveTimeAttack(buf);
}
saveTimeAttack2.msgCode = 0x0136;
saveTimeAttack2.msgLen = 0x0080;
export function saveTimeAttack2(buf: Buffer): SaveTimeAttackRequest {
return saveTimeAttack(buf);
}
+13
View File
@@ -0,0 +1,13 @@
import { SaveTopicRequest } from "../request/saveTopic";
saveTopic.msgCode = 0x009a;
saveTopic.msgLen = 0x0010;
export function saveTopic(buf: Buffer): SaveTopicRequest {
const aimeId = buf.readUInt32LE(0x0004);
return {
type: "save_topic_req",
aimeId: aimeId !== 0xffffffff ? aimeId : undefined,
};
}
+12
View File
@@ -0,0 +1,12 @@
import { UnlockProfileRequest } from "../request/unlockProfile";
unlockProfile.msgCode = 0x006f;
unlockProfile.msgLen = 0x0020;
export function unlockProfile(buf: Buffer): UnlockProfileRequest {
return {
type: "unlock_profile_req",
aimeId: buf.readUInt32LE(0x0004),
pcbId: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"),
};
}
@@ -0,0 +1,13 @@
import { UpdateProvisionalStoreRankRequest } from "../request/updateProvisionalStoreRank";
updateProvisionalStoreRank.msgCode = 0x0082;
updateProvisionalStoreRank.msgLen = 0x0010;
export function updateProvisionalStoreRank(
buf: Buffer
): UpdateProvisionalStoreRankRequest {
return {
type: "update_provisional_store_rank_req",
aimeId: buf.readUInt32LE(0x0004),
};
}
+23
View File
@@ -0,0 +1,23 @@
import { UpdateResultRequest } from "../request/updateResult";
updateResult.msgCode = 0x00cc;
updateResult.msgLen = 0x0030;
export function updateResult(buf: Buffer): UpdateResultRequest {
return {
type: "update_result_req",
field_0002: buf.readUInt16LE(0x0002),
field_0004: buf.readUInt32LE(0x0004),
field_0008: buf.readUInt32LE(0x0008),
field_000C: buf.readUInt32LE(0x000c),
field_0010: buf.readUInt32LE(0x0010),
field_0014: buf.readUInt16LE(0x0014),
field_0016: buf.readUInt16LE(0x0016),
field_0018: buf.readUInt8(0x0018),
field_0019: buf.readUInt8(0x0019),
field_001A: buf.readUInt8(0x001a),
field_001C: buf.readUInt16LE(0x001c),
field_0020: buf.readUInt32LE(0x0020),
field_0024: buf.readUInt8(0x0024),
};
}
@@ -0,0 +1,28 @@
import {
UpdateStoryClearNumRequest1,
UpdateStoryClearNumRequest2,
} from "../request/updateStoryClearNum";
updateStoryClearNum1.msgCode = 0x007f;
updateStoryClearNum1.msgLen = 0x0010;
export function updateStoryClearNum1(
buf: Buffer
): UpdateStoryClearNumRequest1 {
return {
type: "update_story_clear_num_req",
format: 1,
};
}
updateStoryClearNum2.msgCode = 0x013d;
updateStoryClearNum2.msgLen = 0x0010;
export function updateStoryClearNum2(
buf: Buffer
): UpdateStoryClearNumRequest2 {
return {
type: "update_story_clear_num_req",
format: 2,
};
}
@@ -0,0 +1,16 @@
import { ExtId } from "../model/base";
import { Team } from "../model/team";
import { UpdateTeamLeaderRequest } from "../request/updateTeamLeader";
import { AimeId } from "../../../model";
updateTeamLeader.msgCode = 0x008a;
updateTeamLeader.msgLen = 0x0020;
export function updateTeamLeader(buf: Buffer): UpdateTeamLeaderRequest {
return {
type: "update_team_leader_req",
aimeId: buf.readUInt32LE(0x0004) as AimeId,
teamExtId: buf.readUInt32LE(0x0008) as ExtId<Team>,
field_000C: buf.slice(0x000c, buf.indexOf("\0", 0x000c)).toString("ascii"),
};
}
@@ -0,0 +1,16 @@
import { ExtId } from "../model/base";
import { Team } from "../model/team";
import { UpdateTeamMemberRequest } from "../request/updateTeamMember";
import { AimeId } from "../../../model";
updateTeamMember.msgCode = 0x0073;
updateTeamMember.msgLen = 0x0010;
export function updateTeamMember(buf: Buffer): UpdateTeamMemberRequest {
return {
type: "update_team_member_req",
action: buf.readUInt8(0x0004) === 0 ? "add" : "remove",
aimeId: buf.readUInt32LE(0x0008) as AimeId,
teamExtId: buf.readUInt32LE(0x000c) as ExtId<Team>,
};
}
@@ -0,0 +1,12 @@
import { UpdateTeamPointsRequest } from "../request/updateTeamPoints";
updateTeamPoints.msgCode = 0x0081;
updateTeamPoints.msgLen = 0x0010;
export function updateTeamPoints(buf: Buffer): UpdateTeamPointsRequest {
return {
type: "update_team_points_req",
field_0004: buf.readUInt32LE(0x0004),
field_0008: buf.readUInt32LE(0x0008),
};
}
+12
View File
@@ -0,0 +1,12 @@
import { UpdateUiReportRequest } from "../request/updateUiReport";
updateUiReport.msgCode = 0x0084;
updateUiReport.msgLen = 0x0410;
export function updateUiReport(buf: Buffer): UpdateUiReportRequest {
return {
type: "update_ui_report_req",
field_02: buf.readUInt16LE(0x0002),
field_04: buf.readUInt16LE(0x0004),
};
}
+8
View File
@@ -0,0 +1,8 @@
import { UpdateUserLogRequest } from "../request/updateUserLog";
updateUserLog.msgCode = 0x00bd;
updateUserLog.msgLen = 0x0050;
export function updateUserLog(buf: Buffer): UpdateUserLogRequest {
return { type: "update_user_log_req" };
}