idz: Add idz2 encoders

This commit is contained in:
BemaniWitch 2020-10-31 15:50:16 -04:00 committed by Tau
parent 25504006f2
commit 5f708e3372
28 changed files with 637 additions and 92 deletions

View File

@ -1,6 +1,6 @@
import { Chara } from "../model/chara";
export function encodeChara(chara: Chara): Buffer {
export function encodeChara1(chara: Chara): Buffer {
const buf = Buffer.alloc(0x0014);
buf.writeUInt8(chara.gender === "male" ? 0 : 1, 0x00);
@ -16,3 +16,20 @@ export function encodeChara(chara: Chara): Buffer {
return buf;
}
export function encodeChara2(chara: Chara): Buffer {
const buf = Buffer.alloc(0x0016);
buf.writeUInt8(chara.gender === "male" ? 0 : 1, 0x00);
buf.writeUInt16LE(chara.field_02, 0x02);
buf.writeUInt16LE(chara.field_04, 0x04);
buf.writeUInt16LE(chara.field_06, 0x06);
buf.writeUInt16LE(chara.field_08, 0x08);
buf.writeUInt16LE(chara.field_0a, 0x0a);
buf.writeUInt16LE(chara.field_0c, 0x0c);
buf.writeUInt16LE(chara.field_0e, 0x0e);
buf.writeUInt16LE(chara.background, 0x10);
buf.writeUInt16LE(chara.title, 0x14);
return buf;
}

View File

@ -1,7 +1,7 @@
import { writeSjisStr } from "../../util/bin";
import { CreateAutoTeamResponse } from "../response/createAutoTeam";
import { LoadTeamResponse } from "../response/loadTeam";
import { encodeChara } from "./_chara";
import { encodeChara1 } from "./_chara";
function _team(
res: CreateAutoTeamResponse | LoadTeamResponse,
@ -42,7 +42,7 @@ function _team(
buf.writeInt32LE(profile.lv, base + 0x0018);
buf.writeInt32LE(0, base + 0x0024); // Month points, TODO
buf.writeUInt32LE(accessTime, base + 0x0034);
encodeChara(chara).copy(buf, base + 0x0044);
encodeChara1(chara).copy(buf, base + 0x0044);
}
// Team Time Attack:
@ -60,10 +60,18 @@ function _team(
return buf;
}
export function createAutoTeam(res: CreateAutoTeamResponse): Buffer {
export function createAutoTeam1(res: CreateAutoTeamResponse): Buffer {
return _team(res, 0x007c);
}
export function loadTeam(res: LoadTeamResponse): Buffer {
export function loadTeam1(res: LoadTeamResponse): Buffer {
return _team(res, 0x0078);
}
export function createAutoTeam2(res: CreateAutoTeamResponse): Buffer {
return _team(res, 0x0078);
}
export function loadTeam2(res: LoadTeamResponse): Buffer {
return _team(res, 0x0074);
}

View File

@ -1,6 +1,6 @@
import { CheckTeamNameResponse } from "../response/checkTeamName";
export function checkTeamName(res: CheckTeamNameResponse): Buffer {
export function checkTeamName1(res: CheckTeamNameResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x00a3, 0x0000);
@ -8,3 +8,12 @@ export function checkTeamName(res: CheckTeamNameResponse): Buffer {
return buf;
}
export function checkTeamName2(res: CheckTeamNameResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0098, 0x0000);
buf.writeUInt32LE(res.status, 0x0004);
return buf;
}

View File

@ -1,6 +1,6 @@
import { CreateProfileResponse } from "../response/createProfile";
export function createProfile(res: CreateProfileResponse) {
export function createProfile1(res: CreateProfileResponse) {
const buf = Buffer.alloc(0x0020);
// Shares message type code with the "generic" response
@ -9,3 +9,12 @@ export function createProfile(res: CreateProfileResponse) {
return buf;
}
export function createProfile2(res: CreateProfileResponse) {
const buf = Buffer.alloc(0x0030);
buf.writeInt16LE(0x0001, 0x0000);
buf.writeInt32LE(res.aimeId, 0x0008);
return buf;
}

View File

@ -1,6 +1,6 @@
import { CreateTeamResponse } from "../response/createTeam";
export function createTeam(res: CreateTeamResponse): Buffer {
export function createTeam1(res: CreateTeamResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0072, 0x0000);
@ -9,3 +9,13 @@ export function createTeam(res: CreateTeamResponse): Buffer {
return buf;
}
export function createTeam2(res: CreateTeamResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x006e, 0x0000);
buf.writeUInt32LE(res.status, 0x0004);
buf.writeUInt32LE(res.teamExtId, 0x0008);
return buf;
}

View File

@ -1,6 +1,6 @@
import { DiscoverProfileResponse } from "../response/discoverProfile";
export function discoverProfile(res: DiscoverProfileResponse) {
export function discoverProfile1(res: DiscoverProfileResponse) {
const buf = Buffer.alloc(0x0010);
buf.writeInt16LE(0x006c, 0x0000);
@ -8,3 +8,12 @@ export function discoverProfile(res: DiscoverProfileResponse) {
return buf;
}
export function discoverProfile2(res: DiscoverProfileResponse) {
const buf = Buffer.alloc(0x0010);
buf.writeInt16LE(0x0068, 0x0000);
buf.writeInt8(res.exists ? 1 : 0, 0x0004);
return buf;
}

View File

@ -1,40 +1,49 @@
import logger from "debug";
import { createAutoTeam, loadTeam } from "./_team";
import { checkTeamName } from "./checkTeamName";
import { createProfile } from "./createProfile";
import { createTeam } from "./createTeam";
import { discoverProfile } from "./discoverProfile";
import {
createAutoTeam1,
createAutoTeam2,
loadTeam1,
loadTeam2,
} from "./_team";
import { checkTeamName1, checkTeamName2 } from "./checkTeamName";
import { createProfile1, createProfile2 } from "./createProfile";
import { createTeam1, createTeam2 } from "./createTeam";
import { discoverProfile1, discoverProfile2 } from "./discoverProfile";
import { generic } from "./generic";
import { lockProfile } from "./lockProfile";
import { lockProfileExtend } from "./lockProfileExtend";
import { load2on2_v1, load2on2_v2 } from "./load2on2";
import { lockProfile1, lockProfile2 } from "./lockProfile";
import { lockProfileExtend1, lockProfileExtend2 } from "./lockProfileExtend";
import { load2on2_v1, load2on2_v2, load2on2_v3 } from "./load2on2";
import { loadConfig1, loadConfig2 } from "./loadConfig";
import { loadEventInfo } from "./loadEventInfo";
import { loadGacha } from "./loadGacha";
import { loadGarage } from "./loadGarage";
import { loadEventInfo1, loadEventInfo2 } from "./loadEventInfo";
import { loadGacha1, loadGacha2 } from "./loadGacha";
import { loadGarage1, loadGarage2 } from "./loadGarage";
import { loadGeneralReward } from "./loadGeneralReward";
import { loadGhost } from "./loadGhost";
import { loadProfile2 } from "./loadProfile2";
import { loadProfile3 } from "./loadProfile3";
import { loadProfile4 } from "./loadProfile4";
import { loadRewardTable } from "./loadRewardTable";
import { loadServerList } from "./loadServerList";
import { loadStocker } from "./loadStocker";
import { loadTeamRanking } from "./loadTeamRanking";
import { loadTopTen } from "./loadTopTen";
import { loadStocker1, loadStocker2 } from "./loadStocker";
import { loadTeamRanking1, loadTeamRanking2 } from "./loadTeamRanking";
import { loadTopTen } from "./loadTopTen1";
import { saveExpedition1, saveExpedition2 } from "./saveExpedition";
import { saveGarage } from "./saveGarage";
import { saveNewCar } from "./saveNewCar";
import { saveTimeAttack } from "./saveTimeAttack";
import { saveTopic } from "./saveTopic";
import { unlockProfile } from "./unlockProfile";
import { updateProvisionalStoreRank } from "./updateProvisionalStoreRank";
import { saveGarage1, saveGarage2 } from "./saveGarage";
import { saveNewCar1, saveNewCar2 } from "./saveNewCar";
import { saveTimeAttack1, saveTimeAttack2 } from "./saveTimeAttack";
import { saveTopic1, saveTopic2 } from "./saveTopic";
import { unlockProfile1, unlockProfile2 } from "./unlockProfile";
import {
updateProvisionalStoreRank1,
updateProvisionalStoreRank2,
} from "./updateProvisionalStoreRank";
import {
updateStoryClearNum1,
updateStoryClearNum2,
} from "./updateStoryClearNum";
import { updateTeamLeader } from "./updateTeamLeader";
import { updateTeamMember } from "./updateTeamMember";
import { updateTeamLeader1, updateTeamLeader2 } from "./updateTeamLeader";
import { updateTeamMember1, updateTeamMember2 } from "./updateTeamMember";
import { Response } from "../response";
import { ClientHello } from "../../common";
@ -43,19 +52,19 @@ const debug = logger("app:idz:userdb:encoder");
function encode110(res: Response): Buffer {
switch (res.type) {
case "check_team_name_res":
return checkTeamName(res);
return checkTeamName1(res);
case "create_auto_team_res":
return createAutoTeam(res);
return createAutoTeam1(res);
case "create_profile_res":
return createProfile(res);
return createProfile1(res);
case "create_team_res":
return createTeam(res);
return createTeam1(res);
case "discover_profile_res":
return discoverProfile(res);
return discoverProfile1(res);
case "generic_res":
return generic(res);
@ -67,13 +76,13 @@ function encode110(res: Response): Buffer {
return loadConfig1(res);
case "load_event_info_res":
return loadEventInfo(res);
return loadEventInfo1(res);
case "load_gacha_res":
return loadGacha(res);
return loadGacha1(res);
case "load_garage_res":
return loadGarage(res);
return loadGarage1(res);
case "load_general_reward_res":
return loadGeneralReward(res);
@ -91,52 +100,52 @@ function encode110(res: Response): Buffer {
return loadServerList(res);
case "load_stocker_res":
return loadStocker(res);
return loadStocker1(res);
case "load_team_res":
return loadTeam(res);
return loadTeam1(res);
case "load_team_ranking_res":
return loadTeamRanking(res);
return loadTeamRanking1(res);
case "load_top_ten_res":
return loadTopTen(res);
case "lock_profile_extend_res":
return lockProfileExtend(res);
return lockProfileExtend1(res);
case "lock_profile_res":
return lockProfile(res);
return lockProfile1(res);
case "save_expedition_res":
return saveExpedition1(res);
case "save_garage_res":
return saveGarage(res);
return saveGarage1(res);
case "save_new_car_res":
return saveNewCar(res);
return saveNewCar1(res);
case "save_time_attack_res":
return saveTimeAttack(res);
return saveTimeAttack1(res);
case "unlock_profile_res":
return unlockProfile(res);
return unlockProfile1(res);
case "update_provisional_store_rank_res":
return updateProvisionalStoreRank(res);
return updateProvisionalStoreRank1(res);
case "update_story_clear_num_res":
return updateStoryClearNum1(res);
case "update_team_leader_res":
return updateTeamLeader(res);
return updateTeamLeader1(res);
case "update_team_member_res":
return updateTeamMember(res);
return updateTeamMember1(res);
case "save_topic_res":
return saveTopic(res);
return saveTopic1(res);
default:
const exhaustCheck: never = res;
@ -148,19 +157,19 @@ function encode110(res: Response): Buffer {
function encode130(res: Response): Buffer {
switch (res.type) {
case "check_team_name_res":
return checkTeamName(res);
return checkTeamName1(res);
case "create_auto_team_res":
return createAutoTeam(res);
return createAutoTeam1(res);
case "create_profile_res":
return createProfile(res);
return createProfile1(res);
case "create_team_res":
return createTeam(res);
return createTeam1(res);
case "discover_profile_res":
return discoverProfile(res);
return discoverProfile1(res);
case "generic_res":
return generic(res);
@ -172,13 +181,118 @@ function encode130(res: Response): Buffer {
return loadConfig2(res);
case "load_event_info_res":
return loadEventInfo(res);
return loadEventInfo2(res);
case "load_gacha_res":
return loadGacha(res);
return loadGacha2(res);
case "load_garage_res":
return loadGarage(res);
return loadGarage2(res);
case "load_general_reward_res":
return loadGeneralReward(res);
case "load_ghost_res":
return loadGhost(res);
case "load_profile_res":
return loadProfile4(res);
case "load_reward_table_res":
return loadRewardTable(res);
case "load_server_list_res":
return loadServerList(res);
case "load_stocker_res":
return loadStocker2(res);
case "load_team_res":
return loadTeam2(res);
case "load_team_ranking_res":
return loadTeamRanking2(res);
case "load_top_ten_res":
return loadTopTen(res);
case "lock_profile_extend_res":
return lockProfileExtend2(res);
case "lock_profile_res":
return lockProfile2(res);
case "save_expedition_res":
return saveExpedition2(res);
case "save_garage_res":
return saveGarage2(res);
case "save_new_car_res":
return saveNewCar2(res);
case "save_time_attack_res":
return saveTimeAttack2(res);
case "unlock_profile_res":
return unlockProfile2(res);
case "update_provisional_store_rank_res":
return updateProvisionalStoreRank2(res);
case "update_story_clear_num_res":
return updateStoryClearNum2(res);
case "update_team_leader_res":
return updateTeamLeader2(res);
case "update_team_member_res":
return updateTeamMember2(res);
case "save_topic_res":
return saveTopic2(res);
default:
const exhaustCheck: never = res;
throw new Error(`No writer fn for ${res["type"]}`);
}
}
function encode210(res: Response): Buffer {
switch (res.type) {
case "check_team_name_res":
return checkTeamName2(res);
case "create_auto_team_res":
return createAutoTeam2(res);
case "create_profile_res":
return createProfile2(res);
case "create_team_res":
return createTeam2(res);
case "discover_profile_res":
return discoverProfile2(res);
case "generic_res":
return generic(res);
case "load_2on2_res":
return load2on2_v3(res);
case "load_config_res":
return loadConfig2(res);
case "load_event_info_res":
return loadEventInfo1(res);
case "load_gacha_res":
return loadGacha1(res);
case "load_garage_res":
return loadGarage1(res);
case "load_general_reward_res":
return loadGeneralReward(res);
@ -196,52 +310,52 @@ function encode130(res: Response): Buffer {
return loadServerList(res);
case "load_stocker_res":
return loadStocker(res);
return loadStocker1(res);
case "load_team_res":
return loadTeam(res);
return loadTeam1(res);
case "load_team_ranking_res":
return loadTeamRanking(res);
return loadTeamRanking1(res);
case "load_top_ten_res":
return loadTopTen(res);
case "lock_profile_extend_res":
return lockProfileExtend(res);
return lockProfileExtend1(res);
case "lock_profile_res":
return lockProfile(res);
return lockProfile1(res);
case "save_expedition_res":
return saveExpedition2(res);
case "save_garage_res":
return saveGarage(res);
return saveGarage1(res);
case "save_new_car_res":
return saveNewCar(res);
return saveNewCar1(res);
case "save_time_attack_res":
return saveTimeAttack(res);
return saveTimeAttack1(res);
case "unlock_profile_res":
return unlockProfile(res);
return unlockProfile1(res);
case "update_provisional_store_rank_res":
return updateProvisionalStoreRank(res);
return updateProvisionalStoreRank1(res);
case "update_story_clear_num_res":
return updateStoryClearNum2(res);
case "update_team_leader_res":
return updateTeamLeader(res);
return updateTeamLeader1(res);
case "update_team_member_res":
return updateTeamMember(res);
return updateTeamMember1(res);
case "save_topic_res":
return saveTopic(res);
return saveTopic1(res);
default:
const exhaustCheck: never = res;
@ -258,6 +372,9 @@ function encode(res: Response, clientHello: ClientHello) {
case "130":
return encode130(res);
case "210":
return encode210(res);
default:
throw new Error(`Unsupported protocol version ${clientHello.protocol}`);
}

View File

@ -16,3 +16,11 @@ export function load2on2_v2(res: Load2on2Response): Buffer {
return buf;
}
export function load2on2_v3(res: Load2on2Response): Buffer {
const buf = Buffer.alloc(0x1290);
buf.writeInt16LE(0x00a4, 0x0000);
return buf;
}

View File

@ -1,9 +1,17 @@
import { LoadEventInfoResponse } from "../response/loadEventInfo";
export function loadEventInfo(res: LoadEventInfoResponse): Buffer {
export function loadEventInfo1(res: LoadEventInfoResponse): Buffer {
const buf = Buffer.alloc(0x01b0);
buf.writeUInt16LE(0x00bf, 0x0000);
return buf;
}
export function loadEventInfo2(res: LoadEventInfoResponse): Buffer {
const buf = Buffer.alloc(0x01b0);
buf.writeUInt16LE(0x00ad, 0x0000);
return buf;
}

View File

@ -1,6 +1,6 @@
import { LoadGachaResponse } from "../response/loadGacha";
export function loadGacha(res: LoadGachaResponse): Buffer {
export function loadGacha1(res: LoadGachaResponse): Buffer {
const buf = Buffer.alloc(0x0090);
buf.writeUInt16LE(0x00c2, 0x0000);
@ -8,3 +8,12 @@ export function loadGacha(res: LoadGachaResponse): Buffer {
return buf;
}
export function loadGacha2(res: LoadGachaResponse): Buffer {
const buf = Buffer.alloc(0x0090);
buf.writeUInt16LE(0x00b0, 0x0000);
buf.writeUInt8(res.awardedToday ? 0x01 : 0x00, 0x0004);
return buf;
}

View File

@ -1,7 +1,7 @@
import { encodeCar } from "./_car";
import { LoadGarageResponse } from "../response/loadGarage";
export function loadGarage(res: LoadGarageResponse): Buffer {
export function loadGarage1(res: LoadGarageResponse): Buffer {
const buf = Buffer.alloc(0x03d0);
buf.writeUInt16LE(0x0091, 0x0000);
@ -13,3 +13,16 @@ export function loadGarage(res: LoadGarageResponse): Buffer {
return buf;
}
export function loadGarage2(res: LoadGarageResponse): Buffer {
const buf = Buffer.alloc(0x0420);
buf.writeUInt16LE(0x0088, 0x0000);
buf.writeUInt16LE(res.cars.length, 0x0004);
for (let i = 0; i < res.cars.length; i++) {
encodeCar(res.cars[i]).copy(buf, 0x0008 + 0x0068 * i);
}
return buf;
}

View File

@ -1,6 +1,6 @@
import { encodeBitmap } from "./_bitmap";
import { encodeCar } from "./_car";
import { encodeChara } from "./_chara";
import { encodeChara1 } from "./_chara";
import { encodeMission } from "./_mission";
import { LoadProfileResponse } from "../response/loadProfile";
import { writeSjisStr } from "../../util/bin";
@ -98,7 +98,7 @@ export function loadProfile2(res: LoadProfileResponse) {
buf.writeUInt8(res.story.y, 0x0670);
buf.writeUInt16LE(res.story.x, 0x06bc);
encodeMission(res.missions.solo).copy(buf, 0x06e4);
encodeChara(res.chara).copy(buf, 0x070c);
encodeChara1(res.chara).copy(buf, 0x070c);
encodeBitmap(res.titles, 0xb4).copy(buf, 0x720);
buf.writeUInt8(res.settings.aura, 0x07d6);
buf.writeUInt8(res.settings.paperCup, 0x07d9);

View File

@ -1,6 +1,6 @@
import { encodeBitmap } from "./_bitmap";
import { encodeCar } from "./_car";
import { encodeChara } from "./_chara";
import { encodeChara1 } from "./_chara";
import { encodeMission } from "./_mission";
import { LoadProfileResponse } from "../response/loadProfile";
import { writeSjisStr } from "../../util/bin";
@ -98,7 +98,7 @@ export function loadProfile3(res: LoadProfileResponse) {
buf.writeUInt8(res.story.y, 0x080c);
buf.writeUInt16LE(res.story.x, 0x0828);
encodeMission(res.missions.solo).copy(buf, 0x0858);
encodeChara(res.chara).copy(buf, 0x0880);
encodeChara1(res.chara).copy(buf, 0x0880);
encodeBitmap(res.titles, 0xb4).copy(buf, 0x0894);
buf.writeUInt8(res.settings.aura, 0x094a);
buf.writeUInt8(res.settings.paperCup, 0x094d);

View File

@ -0,0 +1,137 @@
import { encodeBitmap } from "./_bitmap";
import { encodeCar } from "./_car";
import { encodeChara2 } from "./_chara";
import { encodeMission } from "./_mission";
import { LoadProfileResponse } from "../response/loadProfile";
import { writeSjisStr } from "../../util/bin";
export function loadProfile4(res: LoadProfileResponse) {
const buf = Buffer.alloc(0x1360);
buf.writeUInt16LE(0x012e, 0x0000);
buf.writeUInt16LE(res.lv, 0x06f0);
buf.writeUInt32LE(res.exp, 0x06f4);
buf.writeUInt32LE(res.dpoint, 0x070c);
buf.writeUInt32LE(res.fame, 0x0728);
buf.writeUInt32LE(res.aimeId, 0x06dc);
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x0c60);
buf.writeUInt32LE(res.mileage, 0x06e0);
encodeMission(res.missions.solo).copy(buf, 0x0aa0);
encodeChara2(res.chara).copy(buf, 0x0ac8);
encodeBitmap(res.titles, 0xb4).copy(buf, 0x0ade);
buf.writeUInt32LE(res.settings.pack, 0x06fc);
buf.writeUInt8(res.settings.aura, 0x0c57);
buf.writeUInt8(res.settings.paperCup, 0x0c5a);
buf.writeUInt8(res.settings.gauges, 0x0c5b);
buf.writeUInt8(res.settings.drivingStyle, 0x1184);
buf.writeUInt16LE(res.settings.music, 0x06ec);
writeSjisStr(buf, 0x0712, 0x0732, res.name);
encodeCar(res.car).copy(buf, 0x1290);
buf.writeUInt32LE(res.carCount, 0x1288);
buf.writeUInt16LE(res.unlocks.auras, 0x00b0);
buf.writeUInt8(res.unlocks.cup, 0x00b4);
buf.writeUInt32LE(res.unlocks.gauges, 0x00b8);
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x0218);
buf.writeUInt16LE(res.unlocks.music, 0x021c);
buf.writeUInt16LE(res.teamLeader ? 1 : 0, 0x06a0);
// Weekly Missions
buf.writeUInt32LE(
(new Date(res.weeklyMissions.weeklyReset).getTime() / 1000) | 0,
0x1068
);
buf.writeUInt16LE(res.weeklyMissions.weeklyMissionLeft, 0x105a);
buf.writeUInt16LE(res.weeklyMissions.weeklyProgressLeft, 0x105c);
buf.writeUInt16LE(res.weeklyMissions.weeklyParamsLeft, 0x105e);
buf.writeUInt16LE(res.weeklyMissions.weeklyMissionRight, 0x1060);
buf.writeUInt16LE(res.weeklyMissions.weeklyProgressRight, 0x1062);
buf.writeUInt16LE(res.weeklyMissions.weeklyParamsRight, 0x1064);
// send twice, once as new stamps, once as old stamps,
// because we don't keep track of the "new!" state.
encodeBitmap(res.stamps, 0x26).copy(buf, 0x10f8);
encodeBitmap(res.stamps, 0x26).copy(buf, 0x111e);
// Selected Stamps
const selectedStamps = res.selectedStamps;
buf.writeUInt16LE(selectedStamps.stamp01, 0x1144);
buf.writeUInt16LE(selectedStamps.stamp02, 0x1146);
buf.writeUInt16LE(selectedStamps.stamp03, 0x1148);
buf.writeUInt16LE(selectedStamps.stamp04, 0x114a);
const { freeCar, freeContinue } = res.tickets;
if (freeCar) {
buf.writeUInt32LE((freeCar.validFrom.getTime() / 1000) | 0, 0x0214);
}
if (freeContinue) {
buf.writeUInt32LE((freeContinue.validFrom.getTime() / 1000) | 0, 0x0700);
buf.writeUInt32LE((freeContinue.validTo.getTime() / 1000) | 0, 0x0704);
}
// Course plays
for (const [courseId, playCount] of res.coursePlays.entries()) {
if (courseId < 0 || courseId >= 20) {
throw new Error(`Course id out of range: ${courseId}`);
}
buf.writeUInt16LE(playCount, 0x0784 + 2 * courseId);
}
for (const score of res.timeAttack) {
const { routeNo } = score;
buf.writeUInt32LE(
(new Date(score.timestamp).getTime() / 1000) | 0, // Date ctor hack
0x00e4 + routeNo * 4
);
buf.writeUInt16LE(0xffff, 0x0184 + 2 * routeNo); // National rank
buf.writeUInt32LE((score.totalTime * 1000) | 0, 0x0824 + 4 * routeNo); // Total Time in MSEC // DONE
buf.writeUInt8(score.flags, 0x0914 + routeNo); // FLAGS
buf.writeUInt8(score.grade, 0x0a2c + routeNo); // MEDALS
for (let i = 0; i < 3; i++) {
buf.writeUInt16LE(
(score.sectionTimes[i] * 1000) >> 2,
0x093c + 6 * routeNo + 2 * i // SECTION TIMES DONE
);
}
}
// the format has changed quite a bit in v2
// seems like it's just split into the big chapters now
// with more cells of course, enough space for 30ish
// the biggest chapter is chapter 1 with a total of 22 races
// but for "security" let's go with up to 28
// first two bytes at the start seem to declare the chapter number
for (let i = 0; i < 6; i++) {
const row = res.story.rows.get(i);
const rowOffset = 0x0258 + i * 0x7a;
if (row === undefined) {
continue;
}
for (let j = 0; j < 28; j++) {
const cell = row.cells.get(j);
// Four bytes per column
const cellOffset = rowOffset + j * 4;
if (cell === undefined) {
continue;
}
buf.writeUInt8(cell.a, cellOffset + 0);
buf.writeUInt8(cell.b, cellOffset + 1);
buf.writeUInt16LE(cell.c, cellOffset + 2);
}
}
buf.writeUInt8(res.story.y, 0x0a54);
buf.writeUInt16LE(res.story.x, 0x0a70);
return buf;
}

View File

@ -1,6 +1,6 @@
import { LoadTeamRankingResponse } from "../response/loadTeamRanking";
export function loadTeamRanking(res: LoadTeamRankingResponse): Buffer {
export function loadTeamRanking1(res: LoadTeamRankingResponse): Buffer {
const buf = Buffer.alloc(0x0ba0);
buf.writeUInt16LE(0x00ba, 0x0000);
@ -8,3 +8,11 @@ export function loadTeamRanking(res: LoadTeamRankingResponse): Buffer {
return buf;
}
export function loadTeamRanking2(res: LoadTeamRankingResponse): Buffer {
const buf = Buffer.alloc(0x0ba0);
buf.writeUInt16LE(0x00a8, 0x0000);
return buf;
}

View File

@ -0,0 +1,79 @@
import { writeSjisStr } from "../../util/bin";
import { LoadTopTenResponse } from "../response/loadTopTen";
export function loadTopTen2(res: LoadTopTenResponse): Buffer {
const buf = Buffer.alloc(0x1720);
buf.writeUInt16LE(0x00ce, 0x0000);
buf.writeUInt16LE(res.courseCount, 0x0004);
for (let i = 0; i < 3; i++) {
if (i >= res.courses.length) {
break;
}
const course = res.courses[i];
const outerOff = 0x0006 + 0x794 * i;
buf.writeUInt16LE(course.routeNo << 1, outerOff + 0x0002);
buf.writeUInt16LE(course.field_02, outerOff + 0x0004); // Bitmask-y?
if (course.rows.length > 0) {
// Section times for the top result (and only top result) are sent OOB
const best = course.rows[0];
for (let j = 0; j < 3; j++) {
buf.writeUInt32LE(
(best.ta.sectionTimes[j] * 1000) | 0,
outerOff + 0x0004 + 4 * j
);
}
}
for (let j = 0; j < 10; j++) {
if (j >= course.rows.length) {
break;
}
const row = course.rows[j];
const innerOff = outerOff + 0x0012 + 0xc0 * j;
buf.writeUInt32LE(row.field_00, innerOff + 0x0000);
buf.writeUInt32LE((row.ta.totalTime * 1000) | 0, innerOff + 0x0004);
buf.writeUInt32LE(
(row.ta.timestamp.getTime() / 1000) | 0,
innerOff + 0x0008
);
buf.writeUInt16LE(row.ta.carSelector, 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);
writeSjisStr(buf, innerOff + 0x0014, innerOff + 0x0028, row.driverName);
writeSjisStr(buf, innerOff + 0x0028, innerOff + 0x0048, row.team.name);
writeSjisStr(buf, innerOff + 0x0048, innerOff + 0x0074, row.shopName);
buf.writeUInt32LE(row.team.nameBg, innerOff + 0x0074);
buf.writeUInt16LE(row.team.nameFx, innerOff + 0x0078);
buf.writeUInt8(row.field_7C, innerOff + 0x007c);
buf.writeUInt8(row.field_7D, innerOff + 0x007d);
// 66 bytes of empty space at the end. Maybe a string used to live here?
}
}
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);
writeSjisStr(buf, offset + 0x08, offset + 0x1c, trailer.name);
} else {
buf.writeUInt8(0xff, offset + 0x02);
}
}
return buf;
}

View File

@ -6,7 +6,7 @@ const statusCodes = {
obsolete: 0x0002,
};
export function lockProfile(res: LockProfileResponse) {
export function lockProfile1(res: LockProfileResponse) {
const buf = Buffer.alloc(0x0020);
buf.writeInt16LE(0x006a, 0x0000);
@ -16,3 +16,14 @@ export function lockProfile(res: LockProfileResponse) {
return buf;
}
export function lockProfile2(res: LockProfileResponse) {
const buf = Buffer.alloc(0x0020);
buf.writeInt16LE(0x0066, 0x0000);
buf.writeInt8(statusCodes[res.status], 0x0018);
buf.writeInt16LE(res.field_001A, 0x001a);
buf.writeInt32LE(res.lockExpiry.getTime() / 1000, 0x001c);
return buf;
}

View File

@ -1,6 +1,6 @@
import { LockProfileExtendResponse } from "../response/lockProfileExtend";
export function lockProfileExtend(res: LockProfileExtendResponse): Buffer {
export function lockProfileExtend1(res: LockProfileExtendResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x006e, 0x0000);
@ -8,3 +8,12 @@ export function lockProfileExtend(res: LockProfileExtendResponse): Buffer {
return buf;
}
export function lockProfileExtend2(res: LockProfileExtendResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x006a, 0x0000);
buf.writeUInt8(res.status, 0x0004);
return buf;
}

View File

@ -1,6 +1,6 @@
import { SaveGarageResponse } from "../response/saveGarage";
export function saveGarage(res: SaveGarageResponse): Buffer {
export function saveGarage1(res: SaveGarageResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x008f, 0x0000);
@ -8,3 +8,12 @@ export function saveGarage(res: SaveGarageResponse): Buffer {
return buf;
}
export function saveGarage2(res: SaveGarageResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0086, 0x0000);
buf.writeUInt16LE(res.status, 0x0004);
return buf;
}

View File

@ -1,6 +1,6 @@
import { SaveNewCarResponse } from "../response/saveNewCar";
export function saveNewCar(res: SaveNewCarResponse): Buffer {
export function saveNewCar1(res: SaveNewCarResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x007a, 0x0000);
@ -8,3 +8,12 @@ export function saveNewCar(res: SaveNewCarResponse): Buffer {
return buf;
}
export function saveNewCar2(res: SaveNewCarResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0076, 0x0000);
buf.writeUInt8(res.status, 0x0004);
return buf;
}

View File

@ -1,9 +1,17 @@
import { SaveTimeAttackResponse } from "../response/saveTimeAttack";
export function saveTimeAttack(res: SaveTimeAttackResponse): Buffer {
export function saveTimeAttack1(res: SaveTimeAttackResponse): Buffer {
const buf = Buffer.alloc(0x00b0);
buf.writeUInt16LE(0x00ce, 0x0000);
return buf;
}
export function saveTimeAttack2(res: SaveTimeAttackResponse): Buffer {
const buf = Buffer.alloc(0x00f0);
buf.writeUInt16LE(0x00cd, 0x0000);
return buf;
}

View File

@ -1,9 +1,17 @@
import { SaveTopicResponse } from "../response/saveTopic";
export function saveTopic(res: SaveTopicResponse) {
export function saveTopic1(res: SaveTopicResponse) {
const buf = Buffer.alloc(0x05d0);
buf.writeInt16LE(0x009b, 0x0000);
return buf;
}
export function saveTopic2(res: SaveTopicResponse) {
const buf = Buffer.alloc(0x05d0);
buf.writeInt16LE(0x0092, 0x0000);
return buf;
}

View File

@ -1,6 +1,6 @@
import { UnlockProfileResponse } from "../response/unlockProfile";
export function unlockProfile(res: UnlockProfileResponse) {
export function unlockProfile1(res: UnlockProfileResponse) {
const buf = Buffer.alloc(0x0010);
buf.writeInt16LE(0x0070, 0x0000);
@ -8,3 +8,12 @@ export function unlockProfile(res: UnlockProfileResponse) {
return buf;
}
export function unlockProfile2(res: UnlockProfileResponse) {
const buf = Buffer.alloc(0x0010);
buf.writeInt16LE(0x006c, 0x0000);
buf.writeInt8(res.status, 0x0004);
return buf;
}

View File

@ -1,7 +1,7 @@
import { UpdateProvisionalStoreRankResponse } from "../response/updateProvisionalStoreRank";
import { writeSjisStr } from "../../util/bin";
export function updateProvisionalStoreRank(
export function updateProvisionalStoreRank1(
res: UpdateProvisionalStoreRankResponse
) {
const buf = Buffer.alloc(0x02b0);
@ -22,3 +22,25 @@ export function updateProvisionalStoreRank(
return buf;
}
export function updateProvisionalStoreRank2(
res: UpdateProvisionalStoreRankResponse
) {
const buf = Buffer.alloc(0x02b0);
buf.writeInt16LE(0x007d, 0x0000);
for (let i = 0; i < 10; i++) {
const offset = 0x44 + i;
const row = res.rows[i];
if (row !== undefined) {
buf.writeInt16LE(row.field_0000, offset + 0x0000);
buf.writeInt32LE(row.field_0004, offset + 0x0004);
writeSjisStr(buf, offset + 0x0010, offset + 0x003b, row.field_0010);
writeSjisStr(buf, offset + 0x003b, offset + 0x0044, row.field_003B);
}
}
return buf;
}

View File

@ -1,6 +1,6 @@
import { UpdateTeamLeaderResponse } from "../response/updateTeamLeader";
export function updateTeamLeader(res: UpdateTeamLeaderResponse): Buffer {
export function updateTeamLeader1(res: UpdateTeamLeaderResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x008b, 0x0000);
@ -8,3 +8,12 @@ export function updateTeamLeader(res: UpdateTeamLeaderResponse): Buffer {
return buf;
}
export function updateTeamLeader2(res: UpdateTeamLeaderResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0084, 0x0000);
buf.writeUInt32LE(res.status, 0x0004);
return buf;
}

View File

@ -1,6 +1,6 @@
import { UpdateTeamMemberResponse } from "../response/updateTeamMember";
export function updateTeamMember(res: UpdateTeamMemberResponse): Buffer {
export function updateTeamMember1(res: UpdateTeamMemberResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0074, 0x0000);
@ -8,3 +8,12 @@ export function updateTeamMember(res: UpdateTeamMemberResponse): Buffer {
return buf;
}
export function updateTeamMember2(res: UpdateTeamMemberResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x0070, 0x0000);
buf.writeUInt32LE(res.status, 0x0004);
return buf;
}

View File

@ -1,7 +1,8 @@
import { BackgroundCode } from "../model/base";
import { BackgroundCode, MyCharaCode } from "../model/base";
export interface LoadStockerResponse {
type: "load_stocker_res";
status: number;
backgrounds: Set<BackgroundCode>;
myChara: Set<MyCharaCode>;
}