diff --git a/src/idz/userdb/encoder/_chara.ts b/src/idz/userdb/encoder/_chara.ts index 5a58051..444495c 100644 --- a/src/idz/userdb/encoder/_chara.ts +++ b/src/idz/userdb/encoder/_chara.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/_team.ts b/src/idz/userdb/encoder/_team.ts index a82e405..f373d80 100644 --- a/src/idz/userdb/encoder/_team.ts +++ b/src/idz/userdb/encoder/_team.ts @@ -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); +} diff --git a/src/idz/userdb/encoder/checkTeamName.ts b/src/idz/userdb/encoder/checkTeamName.ts index 0ae229e..bc547fa 100644 --- a/src/idz/userdb/encoder/checkTeamName.ts +++ b/src/idz/userdb/encoder/checkTeamName.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/createProfile.ts b/src/idz/userdb/encoder/createProfile.ts index 006db5b..a44aa89 100644 --- a/src/idz/userdb/encoder/createProfile.ts +++ b/src/idz/userdb/encoder/createProfile.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/createTeam.ts b/src/idz/userdb/encoder/createTeam.ts index 404a8b8..855f02e 100644 --- a/src/idz/userdb/encoder/createTeam.ts +++ b/src/idz/userdb/encoder/createTeam.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/discoverProfile.ts b/src/idz/userdb/encoder/discoverProfile.ts index acb931a..c3199a5 100644 --- a/src/idz/userdb/encoder/discoverProfile.ts +++ b/src/idz/userdb/encoder/discoverProfile.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/index.ts b/src/idz/userdb/encoder/index.ts index db3cfa6..cea3530 100644 --- a/src/idz/userdb/encoder/index.ts +++ b/src/idz/userdb/encoder/index.ts @@ -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}`); } diff --git a/src/idz/userdb/encoder/load2on2.ts b/src/idz/userdb/encoder/load2on2.ts index 8321a05..6e422e6 100644 --- a/src/idz/userdb/encoder/load2on2.ts +++ b/src/idz/userdb/encoder/load2on2.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/loadEventInfo.ts b/src/idz/userdb/encoder/loadEventInfo.ts index eea840f..744877c 100644 --- a/src/idz/userdb/encoder/loadEventInfo.ts +++ b/src/idz/userdb/encoder/loadEventInfo.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/loadGacha.ts b/src/idz/userdb/encoder/loadGacha.ts index 12d7478..353f7aa 100644 --- a/src/idz/userdb/encoder/loadGacha.ts +++ b/src/idz/userdb/encoder/loadGacha.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/loadGarage.ts b/src/idz/userdb/encoder/loadGarage.ts index 6d22551..aff9271 100644 --- a/src/idz/userdb/encoder/loadGarage.ts +++ b/src/idz/userdb/encoder/loadGarage.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/loadProfile2.ts b/src/idz/userdb/encoder/loadProfile2.ts index 4ab8757..6c3e073 100644 --- a/src/idz/userdb/encoder/loadProfile2.ts +++ b/src/idz/userdb/encoder/loadProfile2.ts @@ -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); diff --git a/src/idz/userdb/encoder/loadProfile3.ts b/src/idz/userdb/encoder/loadProfile3.ts index cbb543f..eb395fc 100644 --- a/src/idz/userdb/encoder/loadProfile3.ts +++ b/src/idz/userdb/encoder/loadProfile3.ts @@ -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); diff --git a/src/idz/userdb/encoder/loadProfile4.ts b/src/idz/userdb/encoder/loadProfile4.ts new file mode 100644 index 0000000..80f009c --- /dev/null +++ b/src/idz/userdb/encoder/loadProfile4.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/loadTeamRanking.ts b/src/idz/userdb/encoder/loadTeamRanking.ts index fb646e7..3b6bbae 100644 --- a/src/idz/userdb/encoder/loadTeamRanking.ts +++ b/src/idz/userdb/encoder/loadTeamRanking.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/loadTopTen.ts b/src/idz/userdb/encoder/loadTopTen1.ts similarity index 100% rename from src/idz/userdb/encoder/loadTopTen.ts rename to src/idz/userdb/encoder/loadTopTen1.ts diff --git a/src/idz/userdb/encoder/loadTopTen2.ts b/src/idz/userdb/encoder/loadTopTen2.ts new file mode 100644 index 0000000..4829257 --- /dev/null +++ b/src/idz/userdb/encoder/loadTopTen2.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/lockProfile.ts b/src/idz/userdb/encoder/lockProfile.ts index 051b41f..1b41d66 100644 --- a/src/idz/userdb/encoder/lockProfile.ts +++ b/src/idz/userdb/encoder/lockProfile.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/lockProfileExtend.ts b/src/idz/userdb/encoder/lockProfileExtend.ts index da1c797..4d31330 100644 --- a/src/idz/userdb/encoder/lockProfileExtend.ts +++ b/src/idz/userdb/encoder/lockProfileExtend.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/saveGarage.ts b/src/idz/userdb/encoder/saveGarage.ts index 6310b2f..d474c6c 100644 --- a/src/idz/userdb/encoder/saveGarage.ts +++ b/src/idz/userdb/encoder/saveGarage.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/saveNewCar.ts b/src/idz/userdb/encoder/saveNewCar.ts index c8a3f9d..f6de538 100644 --- a/src/idz/userdb/encoder/saveNewCar.ts +++ b/src/idz/userdb/encoder/saveNewCar.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/saveTimeAttack.ts b/src/idz/userdb/encoder/saveTimeAttack.ts index f64b91c..b08e3a1 100644 --- a/src/idz/userdb/encoder/saveTimeAttack.ts +++ b/src/idz/userdb/encoder/saveTimeAttack.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/saveTopic.ts b/src/idz/userdb/encoder/saveTopic.ts index 53b1b0f..faa697c 100644 --- a/src/idz/userdb/encoder/saveTopic.ts +++ b/src/idz/userdb/encoder/saveTopic.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/unlockProfile.ts b/src/idz/userdb/encoder/unlockProfile.ts index 30c758b..7eecc3e 100644 --- a/src/idz/userdb/encoder/unlockProfile.ts +++ b/src/idz/userdb/encoder/unlockProfile.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/updateProvisionalStoreRank.ts b/src/idz/userdb/encoder/updateProvisionalStoreRank.ts index 23b9a43..c2c8215 100644 --- a/src/idz/userdb/encoder/updateProvisionalStoreRank.ts +++ b/src/idz/userdb/encoder/updateProvisionalStoreRank.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/updateTeamLeader.ts b/src/idz/userdb/encoder/updateTeamLeader.ts index b0c7ccc..3317329 100644 --- a/src/idz/userdb/encoder/updateTeamLeader.ts +++ b/src/idz/userdb/encoder/updateTeamLeader.ts @@ -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; +} diff --git a/src/idz/userdb/encoder/updateTeamMember.ts b/src/idz/userdb/encoder/updateTeamMember.ts index 11dd1b3..5dc1f48 100644 --- a/src/idz/userdb/encoder/updateTeamMember.ts +++ b/src/idz/userdb/encoder/updateTeamMember.ts @@ -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; +} diff --git a/src/idz/userdb/response/loadStocker.ts b/src/idz/userdb/response/loadStocker.ts index ccd4a33..9f5a3fd 100644 --- a/src/idz/userdb/response/loadStocker.ts +++ b/src/idz/userdb/response/loadStocker.ts @@ -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; + myChara: Set; }