Map out LoadTopTen req and res

This commit is contained in:
Tau
2019-04-28 11:15:56 -04:00
parent e93abfeae1
commit 1a3a5d8ede
5 changed files with 141 additions and 1 deletions

View File

@@ -1,11 +1,33 @@
import { RequestCode } from "./_defs";
import { Id } from "../model/base";
import { Profile } from "../model/profile";
import { Team } from "../model/team";
import { LoadTopTenRequest } from "../request/loadTopTen";
loadTopTen.msgCode = 0x00b5 as RequestCode;
loadTopTen.msgLen = 0x00e0;
export function loadTopTen(buf: Buffer): LoadTopTenRequest {
const selectors = new Array();
for (let i = 0; i < 32; i++) {
selectors.push({
field_4: buf.readUInt16LE(0x0004 + 2 * i),
field_44: buf.readUInt32LE(0x0044 + 4 * i),
});
}
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),
profileId: profileId !== 0 ? (profileId as Id<Profile>) : undefined,
teamId: teamId !== 0xffffffff ? (teamId as Id<Team>) : undefined,
};
}

View File

@@ -1,10 +1,71 @@
import iconv = require("iconv-lite");
import { LoadTopTenResponse } from "../response/loadTopTen";
export function loadTopTen(res: LoadTopTenResponse): Buffer {
const buf = Buffer.alloc(0x1720);
buf.writeUInt16LE(0x00b6, 0x0000);
// in awe of the size of this lad too
buf.writeUInt16LE(res.totalSelected, 0x0002);
for (let i = 0; i < 3; i++) {
if (i >= res.courses.length) {
break;
}
const course = res.courses[i];
const outerOff = 0x0004 + 0x794 * i;
buf.writeUInt16LE(course.courseId, outerOff + 0x0000);
buf.writeUInt16LE(course.field_02, outerOff + 0x0002); // Bitmask-y?
buf.writeUInt32LE(course.wrStageMsec[0], outerOff + 0x0004);
buf.writeUInt32LE(course.wrStageMsec[1], outerOff + 0x0008);
buf.writeUInt32LE(course.wrStageMsec[2], outerOff + 0x000c);
for (let j = 0; j < 10; j++) {
if (j >= course.rows.length) {
break;
}
const row = course.rows[i];
const innerOff = outerOff + 0x0010 + 0xc0 * j;
buf.writeUInt32LE(row.field_00, innerOff + 0x0000);
buf.writeUInt32LE(row.totalMsec, innerOff + 0x0004);
buf.writeUInt32LE(
(row.timestamp.getTime() / 1000) | 0,
innerOff + 0x0008
);
buf.writeUInt16LE(row.car, innerOff + 0x000c); // CAR
buf.writeUInt8(row.field_0E ? 1 : 0, innerOff + 0x000e); // Boolean
buf.writeUInt8(row.field_0F ? 1 : 0, innerOff + 0x000f); // Boolean
buf.writeUInt8(row.field_10, innerOff + 0x0010);
iconv.encode(row.driverName, "shift_jis").copy(buf, innerOff + 0x0014);
iconv.encode(row.teamName, "shift_jis").copy(buf, innerOff + 0x0028);
iconv.encode(row.shopName, "shift_jis").copy(buf, innerOff + 0x0048);
buf.writeUInt32LE(row.field_74, innerOff + 0x0074);
buf.writeUInt16LE(row.field_78, innerOff + 0x0078);
buf.writeUInt8(row.field_7C, innerOff + 0x007c);
buf.writeUInt8(row.field_7D, innerOff + 0x007d);
// 66 bytes of fucking nothing what
}
}
for (let i = 0; i < 3; i++) {
const offset = 0x16c0 + 0x1c * i;
if (i < res.trailers.length) {
const trailer = res.trailers[i];
buf.writeUInt16LE(trailer.yearMonth, offset + 0x00);
buf.writeUInt8(trailer.courseId, offset + 0x02);
buf.writeUInt8(trailer.isNight ? 1 : 0, offset + 0x03);
buf.writeUInt32LE(trailer.totalMsec, offset + 0x04);
iconv.encode(trailer.name, "shift_jis").copy(buf, offset + 0x08);
} else {
buf.writeUInt8(0xff, offset + 0x02);
}
}
return buf;
}

View File

@@ -8,5 +8,8 @@ export function loadTopTen(
): LoadTopTenResponse {
return {
type: "load_top_ten_res",
totalSelected: 0,
courses: [],
trailers: [],
};
}

View File

@@ -1,3 +1,19 @@
import { Id } from "../model/base";
import { Profile } from "../model/profile";
import { Team } from "../model/team";
export interface LoadTopTenRequestSelector {
field_4: number;
field_44: number;
}
export interface LoadTopTenRequest {
type: "load_top_ten_req";
field_2: number;
selectors: LoadTopTenRequestSelector[];
field_C4: number;
field_C5: number;
field_C6: number;
profileId?: Id<Profile>;
teamId?: Id<Team>;
}

View File

@@ -1,3 +1,41 @@
import { CarSelector } from "../model/car";
export interface LoadTopTenResponseRow {
field_00: number;
totalMsec: number;
timestamp: Date;
car: CarSelector;
field_0E: boolean;
field_0F: boolean;
field_10: number;
driverName: string;
teamName: string;
shopName: string;
field_74: number;
field_78: number;
field_7C: number;
field_7D: number;
}
export interface LoadTopTenResponseCourse {
courseId: number; // Multiplied by 2! Includes day/night i guess
field_02: number;
wrStageMsec: number[]; // Sent OOB, for first-place only
rows: LoadTopTenResponseRow[];
}
export interface LoadTopTenResponseTrailer {
// Something team related...
yearMonth: number;
courseId: number;
isNight: boolean;
totalMsec: number;
name: string;
}
export interface LoadTopTenResponse {
type: "load_top_ten_res";
totalSelected: number;
courses: LoadTopTenResponseCourse[];
trailers: LoadTopTenResponseTrailer[];
}