Compare commits

..

78 Commits
v012 ... master

Author SHA1 Message Date
Emi Midnight
87661cf871
check for both null AND undefined in stream writer (#44)
This fixes IDZ support when minime is run using a newer LTS version of
node js.
(Streams use null for the error argument now for write and end
callbacks)
2024-03-19 11:59:08 +01:00
Tau
d6d98aa561 v016 2021-02-04 17:03:11 -05:00
Tau
2c78e6646a Fix trailing comma in SQL 2021-02-04 17:02:23 -05:00
Tau
0f8010ae8d v015 2021-02-04 16:47:21 -05:00
Tau
cb5db5e55f idz: Drop idz_team_auto_uq constraint 2021-02-04 16:41:42 -05:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
a99503a86d idz: Fix team message encoding 2021-02-04 16:32:03 -05:00
Tau
37660a382a v014 2021-02-03 18:51:28 -05:00
Tau
e43faeff10 idz: Fix SqlTeamRepository 2021-02-03 23:43:46 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
2189132753 idz: Fix logic in SqlStoryRepository 2021-02-03 23:43:45 +00:00
Tau
85aad3377e idz: Fix SqlStampsRepository 2021-02-03 23:43:45 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
2f5a63858f idz: SqlSettingsRepository: Persist driving_style 2021-02-03 23:43:45 +00:00
Tau
dbffb15779 idz: Fix SqlProfileRepository 2021-02-03 23:43:45 +00:00
Tau
1f544ddb7e idz: Fix SqlMyCharaRepository 2021-02-03 23:43:45 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
7f47315130 idz: Save weekly missions progress 2021-02-03 23:43:45 +00:00
Tau
afe9f27c25 idz: Fix SqlWeeklyMissionsRepository 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
f8ca0bdcd3 idz: Fix remaining encoder mappings 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
98caa9248b idz: Split/fix loadServerList encoder 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
0236d44466 idz: Split/fix loadRewardTable encoder 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
4c0e933ea4 idz: Split/fix loadGhost encoder 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
8b284107b1 idz: Split/fix "generic" response encoding 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
2c31bd0c47 idz: Fix _team encoders 2021-02-03 23:43:44 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
7b329bc37c idz: Fix unknownA_2 message length rounding 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
0937c0fadf idz: Fix saveGarage processing 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
ea92e4e981 idz: Fix loadProfile codecs 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
1cfd577f56 idz: Fix loadGeneralReward codecs 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
b7187fe0ed idz: Fix updateStoryClearNum codecs 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
60c089a810 idz: Fix saveTimeAttack codecs 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
377096a077 idz: Fix loadTopTen 2021-02-03 23:43:43 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
87de79ef12 idz: Fix loadTeamRanking mapping 2021-02-03 23:43:43 +00:00
Tau
f1386f8e3d idz: Split/fix loadConfig messages 2021-02-03 23:43:42 +00:00
Tau
6d581c114d idz: Split/fix load2on2 messages 2021-02-03 23:43:42 +00:00
Tau
9570349565 idz: Plumb clientHello into request dispatcher 2021-02-03 23:43:42 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
206d12742a idz: Ignore ServerBox traffic 2021-02-03 23:43:42 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
f2434cef7e idz: Add idz2 encoders 2021-02-03 23:43:42 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
2474879c89 idz: Add idz2 decoders 2021-02-03 23:43:42 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
4c36e0abf0 idz: Add LoadStockerResponse.myChara field 2021-02-03 23:43:42 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
ccd5fd72c8 idz: Add field "c" to story cell tuple 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
46499a5425 idz: Add UpdateExpedition command 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
8562da15b6 idz: Add SaveStockerRequest.myChara 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
8195e9f937 idz: Add stamps to SaveStockerRequest 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
9f57ff1342 idz: Add idz_my_chara table 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
a7b7a01685 idz: Add idz_weekly_missions table 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
8ada783f7c idz: Add stamps tables 2021-02-03 23:43:41 +00:00
b166fe48b91eb09f279b79a4ecf57b2f8f0d3705
f79c4a4370 idz: Add idz_settings.driving_style column 2021-02-03 23:43:41 +00:00
Tau
f3be39823a idz: Explicitly split team msg encoders 2021-02-03 23:43:40 +00:00
Tau
03d0e87b55 idz: Push story geometry down into codecs
Sparsify the data model returned from the repository layer. Different
versions of the game can have drastically different dimensions for the
story data grid, so handling those dimensions is best done in the
encoders themselves.
2021-02-03 23:43:40 +00:00
Tau
efcc1caa76 idz: Add explicit CreateProfileResponse 2021-02-03 23:43:40 +00:00
Tau
2a546c0d9d idz: Generalize msg00AD
The purpose of this request is unknown, but its message code
changed in idz2.
2021-02-03 23:43:40 +00:00
Tau
63627eaf78 idz: Combine loadConfig requests 2021-02-03 23:43:40 +00:00
Tau
ed7c461fe1 idz: Dispatch on declared protocol version 2021-02-03 23:43:39 +00:00
Tau
75ac6e03f2 idz: Support multiple major versions
Doesn't actually add support for any additional versions, but it
does lay the groundwork.
2021-02-03 23:43:39 +00:00
Tau
2f2dc82b5a idz: Add type defs for common net comms context 2021-02-03 23:43:38 +00:00
Tau
198256e6ff idz: Add implicit version field to requests 2021-02-03 23:43:38 +00:00
Tau
eb276128c2 sql: Add "maintenance transaction" system
SQLite's ALTER TABLE functionality leaves a lot to be desired, so
in practice the way you alter SQLite schema is to create a new
table, migrate the data into it, then drop the old table. However,
this is potentially complicated by the presence of foreign key
constraints.

In order to resolve the foreign key constraint issue we add a
`maintenance()` method to the `DataSource` abstraction layer. This
operates similarly to the existing `transaction()` method, but the
SQLite implementation of this method defers enforcement of foreign
keys until the transaction is about to commit.
2021-02-03 23:43:38 +00:00
Tau
6090a60467 sql: Drive-by method documentation 2021-02-03 23:43:38 +00:00
Tau
2099622b0f sql: Throw conventionally... 2021-02-03 23:43:38 +00:00
Tau
9494f3026b checkdb: Use single transaction 2021-02-03 23:43:38 +00:00
Tau
f7ceb6bcda idz: Prettier compliance 2021-02-03 23:43:38 +00:00
Tau
04dac7279e idz: Remove some dead code 2021-02-03 23:43:37 +00:00
Tau
8c164b6130 idz: Use string I/O helpers 2021-02-03 23:43:37 +00:00
Tau
27b229e1c2 idz: Remove iffy redundant NUL termination op 2021-02-03 23:43:37 +00:00
Tau
c877d9d214 idz: Fix apparent buffer stride bug 2021-02-03 23:43:37 +00:00
Tau
636789b8bf idz: Delete dead imports 2021-02-03 23:43:37 +00:00
Tau
48aeb1c9d2 types: Declare ON CONFLICT ... DO NOTHING clause 2021-02-03 23:43:36 +00:00
Tau
5f09c00eb4 Add explicit vscode formatter conf 2021-02-03 23:43:36 +00:00
Tau
bf28710000 v013 2020-12-15 18:05:28 -05:00
Tau
8c61bda865 Apply Prettier to CHANGELOG 2020-12-15 18:05:09 -05:00
Felix
1fa268e6ba chunithm: stub GetUserFavoriteMusicApi 2020-12-15 22:59:23 +00:00
e76cd2ec3a63f11710dc9fa0bc5b66176521af0a
4fe69077fc chuni: limit response size of getUserActivity 2020-11-08 23:54:28 -08:00
Tau
4f5eb97309 Fix npm audit noise 2020-09-01 01:10:32 +00:00
Tau
122c397b59 idz: Add utilities for binary I/O 2020-09-01 01:10:32 +00:00
Tau
d3984dcd5d idz: Use Switchboard config for some ports 2020-09-01 01:10:31 +00:00
Tau
4abc04e9f6 idz: Port userdb/ onto common/ 2020-09-01 01:10:31 +00:00
Tau
c01dbb50f7 idz: Factor out common TCP obfuscation 2020-09-01 01:10:31 +00:00
Tau
fa5ed284e1 util: Add promise-based stream wrapper
You'd think something like this would exist on npm somewhere.
2020-09-01 01:10:31 +00:00
Tau
76583f17ce idz: Fix userdb msg 00AD block alignment 2020-09-01 01:10:31 +00:00
Tau
f959993197 idz: Hoist userdb code 2020-09-01 01:10:31 +00:00
Tau
6dceb7c48a Fix CHANGELOG attribution 2020-08-19 18:44:29 -04:00
365 changed files with 8727 additions and 3883 deletions

View File

@ -1,5 +1,8 @@
{
"editor.formatOnSave": true,
"editor.rulers": [79],
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

View File

@ -1,53 +1,70 @@
# CHANGELOG
## v016
- IDZ: Fix SQL syntax error (Tau)
## v015
- IDZ: Team fixes (BemaniWitch)
## v014
- IDZ: Add support for Initial D v2.1 (BemaniWitch)
## v013
- IDZ: Miscellaneous refactoring (Tau)
- Chunithm: Add stubs to support Amazon Plus (Felix)
## v012
* Chunithm: Fix large data loads (NeumPhis)
* Chunithm: Add some support for older versions (NeumPhis)
* Chunithm: Fix large data loads (Matt Bilker)
* Chunithm: Enable all events (Matt Bilker)
* Chunithm: Remove server maintenance period (Matt Bilker)
* Chunithm: Fix data loss (esterTion)
* Chunithm: Add some support for older versions (esterTion)
* IDZ: Team-related fixes (Emi Midnight)
* Add support for newer AIME card services (cxm)
* Chunithm: Enable ticket system (Rob)
* Chunithm: Implement Recent Rating feature (seika1)
* Chunithm: Fix User Activity feature (seika1)
- Chunithm: Fix large data loads (NeumPhis)
- Chunithm: Add some support for older versions (NeumPhis)
- Chunithm: Fix large data loads (Felix)
- Chunithm: Enable all events (Felix)
- Chunithm: Remove server maintenance period (Felix)
- Chunithm: Fix data loss (esterTion)
- Chunithm: Add some support for older versions (esterTion)
- IDZ: Team-related fixes (Emi Midnight)
- Add support for newer AIME card services (cxm)
- Chunithm: Enable ticket system (Rob)
- Chunithm: Implement Recent Rating feature (seika1)
- Chunithm: Fix User Activity feature (seika1)
## v011
* IDZ: Fix loading of >10 cars in garage (Tau)
- IDZ: Fix loading of >10 cars in garage (Tau)
## v010
* Chunithm: Bug fix: Fix new database creation and repair affected databases. Again. (Tau)
Switch to a different SQLite native code extension. C compiler is no longer required. (Tau)
- Chunithm: Bug fix: Fix new database creation and repair affected databases. Again. (Tau)
Switch to a different SQLite native code extension. C compiler is no longer required. (Tau)
## v009
* IDZ: Display team banners in attract loop rankings (Tau)
* Chunithm: Bug fix: Fix new database creation and repair affected databases (Tau)
- IDZ: Display team banners in attract loop rankings (Tau)
- Chunithm: Bug fix: Fix new database creation and repair affected databases (Tau)
## v008
* Implement auras in IDZ (BemaniWitch)
* Fix user ratings (NeumPhis)
* Load and save course results (NeumPhis)
* Load and save user duel list (Felix)
- Implement auras in IDZ (BemaniWitch)
- Fix user ratings (NeumPhis)
- Load and save course results (NeumPhis)
- Load and save user duel list (Felix)
## v007
* Fix IDZ team creation (Tau)
* Fix startup script working directory (Tau)
- Fix IDZ team creation (Tau)
- Fix startup script working directory (Tau)
## v006
* Fix Chunithm HTTP POST size limit (Tau)
- Fix Chunithm HTTP POST size limit (Tau)
## v005
* Initial public release (Tau)
* Chunithm v1.30.00 Amazon
* Initial D Zero v1.21.00
* Initial D Zero v1.31.00
- Initial public release (Tau)
- Chunithm v1.30.00 Amazon
- Initial D Zero v1.21.00
- Initial D Zero v1.31.00

5843
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,12 +27,12 @@
"@types/jest": "^24.0.11",
"@types/multiparty": "^0.0.32",
"@types/node": "^12.11.7",
"jest": "^24.5.0",
"jest-haste-map": "^24.5.0",
"jest": "^26.4.2",
"jest-haste-map": "^26.3.0",
"jest-resolve": "^24.5.0",
"prettier": "^1.16.4",
"ts-jest": "^24.0.0",
"typescript": "^3.6.4",
"ts-jest": "^26.3.0",
"typescript": "^3.8.3",
"utility-types": "^3.6.1"
},
"scripts": {

View File

@ -7,6 +7,10 @@ create table "idz_profile" (
"player_id" integer not null
references "aime_player"("id")
on delete cascade,
-- Major version of Initial D Zero, either 1 or 2.
-- The two major versions are incompatible with each other and do not
-- permit the player to carry progress over from one to the other.
"version" integer not null,
-- TODO shop_id
"name" text not null,
"lv" integer not null,
@ -16,7 +20,7 @@ create table "idz_profile" (
"mileage" integer not null,
"register_time" timestamp not null,
"access_time" timestamp not null,
constraint "idz_profile_player_uq" unique ("player_id")
constraint "idz_profile_player_uq" unique ("player_id", "version")
);
create table "idz_chara" (
@ -100,7 +104,27 @@ create table "idz_settings" (
"pack" integer not null,
"aura" integer not null,
"paper_cup" integer not null, -- Not a boolean, oddly enough
"gauges" integer not null
"gauges" integer not null,
"driving_style" integer not null
);
create table "idz_stamp_selections" (
"id" integer primary key not null
references "idz_profile"("id")
on delete cascade,
"stamp_01" integer not null,
"stamp_02" integer not null,
"stamp_03" integer not null,
"stamp_04" integer not null
);
create table "idz_stamp_unlock" (
"id" integer primary key not null,
"profile_id" integer not null
references "idz_profile"("id")
on delete cascade,
"stamp_no",
constraint "idz_stamp_unlock_uq" unique ("profile_id", "stamp_no")
);
create table "idz_story_state" (
@ -120,6 +144,7 @@ create table "idz_story_cell_state" (
"col_no" integer not null,
"a" integer not null,
"b" integer not null,
"c" integer not null,
constraint "idz_story_cell_state_uq" unique (
"profile_id",
"row_no",
@ -202,12 +227,13 @@ create table "idz_unlocks" (
create table "idz_team" (
"id" integer primary key not null,
"version" integer not null,
"ext_id" integer not null,
"name" text not null,
"name_bg" integer not null,
"name_fx" integer not null,
"register_time" timestamp not null,
constraint "idz_team_uq" unique ("ext_id")
constraint "idz_team_uq" unique ("version", "ext_id")
);
create table "idz_team_auto" (
@ -215,8 +241,7 @@ create table "idz_team_auto" (
references "idz_team"("id")
on delete cascade,
"serial_no" integer not null,
"name_idx" integer not null,
constraint "idz_team_auto_uq" unique ("serial_no", "name_idx")
"name_idx" integer not null
);
create table "idz_team_member" (
@ -240,3 +265,25 @@ create table "idz_team_reservation" (
"join_time" timestamp not null,
"leader" boolean not null
);
create table "idz_weekly_missions" (
"id" integer primary key not null
references "idz_profile"("id")
on delete cascade,
"weekly_reset" timestamp not null,
"mission_left" integer not null,
"progress_left" integer not null,
"params_left" integer not null,
"mission_right" integer not null,
"progress_right" integer not null,
"params_right" integer not null
);
create table "idz_my_chara" (
"id" integer primary key not null,
"profile_id" integer not null
references "idz_profile"("id")
on delete cascade,
"my_chara_no" integer not null,
constraint "idz_my_chara_uq" unique ("profile_id", "my_chara_no")
);

View File

@ -0,0 +1,77 @@
create table "new_idz_profile" (
"id" integer primary key not null,
"player_id" integer not null
references "aime_player"("id")
on delete cascade,
"version" integer not null,
"name" text not null,
"lv" integer not null,
"exp" integer not null,
"fame" integer not null,
"dpoint" integer not null,
"mileage" integer not null,
"register_time" timestamp not null,
"access_time" timestamp not null,
constraint "idz_profile_player_uq" unique ("player_id", "version")
);
create table "new_idz_team" (
"id" integer primary key not null,
"version" integer not null,
"ext_id" integer not null,
"name" text not null,
"name_bg" integer not null,
"name_fx" integer not null,
"register_time" timestamp not null,
constraint "idz_team_uq" unique ("version", "ext_id")
);
insert into "new_idz_profile" (
"id",
"player_id",
"version",
"name",
"lv",
"exp",
"fame",
"dpoint",
"mileage",
"register_time",
"access_time"
) select
x."id",
x."player_id",
1,
x."name",
x."lv",
x."exp",
x."fame",
x."dpoint",
x."mileage",
x."register_time",
x."access_time"
from "idz_profile" as "x";
insert into "new_idz_team" (
"id",
"version",
"ext_id",
"name",
"name_bg",
"name_fx",
"register_time"
) select
x."id",
1,
x."ext_id",
x."name",
x."name_bg",
x."name_fx",
x."register_time"
from "idz_team" as "x";
drop table "idz_profile";
drop table "idz_team";
alter table "new_idz_profile" rename to "idz_profile";
alter table "new_idz_team" rename to "idz_team";

View File

@ -0,0 +1,111 @@
create table "idz_my_chara" (
"id" integer primary key not null,
"profile_id" integer not null
references "idz_profile"("id")
on delete cascade,
"my_chara_no" integer not null,
constraint "idz_my_chara_uq" unique ("profile_id", "my_chara_no")
);
create table "new_idz_settings" (
"id" integer primary key not null
references "idz_profile"("id")
on delete cascade,
"music" integer not null,
"pack" integer not null,
"aura" integer not null,
"paper_cup" integer not null, -- Not a boolean, oddly enough
"gauges" integer not null,
"driving_style" integer not null
);
create table "idz_stamp_selections" (
"id" integer primary key not null
references "idz_profile"("id")
on delete cascade,
"stamp_01" integer not null,
"stamp_02" integer not null,
"stamp_03" integer not null,
"stamp_04" integer not null
);
create table "idz_stamp_unlock" (
"id" integer primary key not null,
"profile_id" integer not null
references "idz_profile"("id")
on delete cascade,
"stamp_no",
constraint "idz_stamp_unlock_uq" unique ("profile_id", "stamp_no")
);
create table "new_idz_story_cell_state" (
"id" integer primary key not null,
"profile_id" integer not null
references "idz_profile"("id")
on delete cascade,
"row_no" integer not null,
"col_no" integer not null,
"a" integer not null,
"b" integer not null,
"c" integer not null,
constraint "idz_story_cell_state_uq" unique (
"profile_id",
"row_no",
"col_no"
)
);
create table "idz_weekly_missions" (
"id" integer primary key not null
references "idz_profile"("id")
on delete cascade,
"weekly_reset" timestamp not null,
"mission_left" integer not null,
"progress_left" integer not null,
"params_left" integer not null,
"mission_right" integer not null,
"progress_right" integer not null,
"params_right" integer not null
);
insert into "new_idz_settings" (
"id",
"music",
"pack",
"aura",
"paper_cup",
"gauges",
"driving_style"
) select
x."id",
x."music",
x."pack",
x."aura",
x."paper_cup",
x."gauges",
0
from "idz_settings" as x;
insert into "new_idz_story_cell_state" (
"id",
"profile_id",
"row_no",
"col_no",
"a",
"b",
"c"
) select
x."id",
x."profile_id",
x."row_no",
x."col_no",
x."a",
x."b",
0
from "idz_story_cell_state" as x;
drop table "idz_settings";
drop table "idz_story_cell_state";
alter table "new_idz_settings" rename to "idz_settings";
alter table "new_idz_story_cell_state" rename to "idz_story_cell_state";

View File

@ -0,0 +1,13 @@
create table "new_idz_team_auto" (
"id" integer primary key not null
references "idz_team"("id")
on delete cascade,
"serial_no" integer not null,
"name_idx" integer not null
);
insert into "new_idz_team_auto"
select "id", "serial_no", "name_idx" from "idz_team_auto";
drop table "idz_team_auto";
alter table "new_idz_team_auto" rename to "idz_team_auto";

View File

@ -104,30 +104,32 @@ async function migratedb(
}
export default async function checkdb(db: DataSource): Promise<void> {
const stmt = sql.select("schemaver").from("meta");
let maybe: number | undefined;
let schemaver: number | undefined;
try {
const row = await db.transaction(txn => txn.fetchRow(stmt));
await db.maintenance(async txn => {
const stmt = sql.select("schemaver").from("meta");
if (row !== undefined) {
maybe = parseInt(row.schemaver!);
try {
const row = await txn.fetchRow(stmt);
if (row !== undefined) {
schemaver = parseInt(row.schemaver!);
}
} catch (e) {
return await initdb(txn);
}
} catch (e) {
return db.transaction(initdb);
}
if (maybe === undefined) {
throw new Error(
"Database corrupted: `meta` table singleton row is missing"
);
}
if (schemaver === undefined) {
throw new Error(
"Database corrupted: `meta` table singleton row is missing"
);
}
const schemaver = maybe;
const newver = await db.transaction(txn => migratedb(txn, schemaver));
schemaver = await migratedb(txn, schemaver);
});
if (newver !== undefined) {
debug("Upgraded database to version %s", newver);
if (schemaver !== undefined) {
debug("Upgraded database to version %s", schemaver);
await db.vacuum();

View File

@ -0,0 +1,23 @@
import { Repositories } from "../repo";
import { GetUserFavoriteMusicRequest } from "../request/getUserFavoriteMusic";
import { GetUserFavoriteMusicResponse } from "../response/getUserFavoriteMusic";
import { readAimeId } from "../proto/base";
export default async function getUserFavoriteMusic(
rep: Repositories,
req: GetUserFavoriteMusicRequest
): Promise<GetUserFavoriteMusicResponse> {
const aimeId = readAimeId(req.userId);
const profileId = await rep.userData().lookup(aimeId);
/*
* `Chunithm Amazon Plus` does not appear to save a favorites list and there
* is no user-accessible favorites function from what I can tell.
*/
return {
userId: req.userId,
length: "0",
userFavoriteMusicList: [],
};
}

View File

@ -18,6 +18,7 @@ import getUserCourse from "./getUserCourse";
import getUserData from "./getUserData";
import getUserDataEx from "./getUserDataEx";
import getUserDuel from "./getUserDuel";
import getUserFavoriteMusic from "./getUserFavoriteMusic";
import getUserItem from "./getUserItem";
import getUserMap from "./getUserMap";
import getUserMusic from "./getUserMusic";
@ -99,6 +100,7 @@ export default function chunithm(db: DataSource) {
wrapper.rpc("/GetUserDataApi", getUserData);
wrapper.rpc("/GetUserDataExApi", getUserDataEx);
wrapper.rpc("/GetUserDuelApi", getUserDuel);
wrapper.rpc("/GetUserFavoriteMusicApi", getUserFavoriteMusic);
wrapper.rpc("/GetUserItemApi", getUserItem);
wrapper.rpc("/GetUserMapApi", getUserMap);
wrapper.rpc("/GetUserMusicApi", getUserMusic);

View File

@ -0,0 +1,4 @@
export interface GetUserFavoriteMusicRequest {
/** Integer, AiMe ID */
userId: string;
}

View File

@ -0,0 +1,10 @@
export interface GetUserFavoriteMusicResponse {
/** Integer, AiMe ID */
userId: string;
/** Integer, number of results returned */
length: string;
/** TBD */
userFavoriteMusicList: [];
}

View File

@ -29,7 +29,8 @@ export class SqlUserActivityRepository implements UserActivityRepository {
.from("cm_user_activity")
.where("profile_id", profileId)
.where("kind", kind)
.orderBy("sort_number DESC");
.orderBy("sort_number DESC")
.limit(100);
const rows = await this._txn.fetchRows(stmt);

43
src/idz/common/aes.ts Normal file
View File

@ -0,0 +1,43 @@
import { Decipher, Cipher, createCipheriv, createDecipheriv } from "crypto";
import { ByteStream } from "../../util/stream";
export const BLOCK_SIZE = 16;
export default class AesEcbStream implements ByteStream {
private readonly _dec: Decipher;
private readonly _enc: Cipher;
constructor(private _stm: ByteStream, keybuf: Buffer) {
this._dec = createDecipheriv("aes-128-ecb", keybuf, null).setAutoPadding(
false
);
this._enc = createCipheriv("aes-128-ecb", keybuf, null).setAutoPadding(
false
);
}
close() {
this._stm.close();
}
async read(nbytes: number): Promise<Buffer> {
if (nbytes % BLOCK_SIZE !== 0) {
throw new Error("Attempted to read partial cipher block");
}
const ciphertext = await this._stm.read(nbytes);
return this._dec.update(ciphertext);
}
async write(buf: Buffer): Promise<void> {
if (buf.length % BLOCK_SIZE !== 0) {
throw new Error("Attempted to write partial cipher block");
}
const ciphertext = this._enc.update(buf);
await this._stm.write(ciphertext);
}
}

View File

@ -1,4 +1,17 @@
export function byteString(n: bigint, length: number) {
export function readBigInt(buf: Buffer) {
let result = 0n;
for (let i = 0; i < buf.length; i++) {
const shift = 8n * BigInt(i);
const byte = buf.readUInt8(i);
result |= BigInt(byte) << shift;
}
return result;
}
export function writeBigInt(n: bigint, length: number) {
const result = Buffer.alloc(length);
for (let i = 0; i < length; i++) {

5
src/idz/common/index.ts Normal file
View File

@ -0,0 +1,5 @@
import setup from "./setup";
export { BLOCK_SIZE } from "./aes";
export { ClientHello } from "./setup";
export default setup;

87
src/idz/common/setup.ts Normal file
View File

@ -0,0 +1,87 @@
import { Socket } from "net";
import AesEcbStream from "./aes";
import { readBigInt, modPow, writeBigInt } from "./bigint";
import IoByteStream, { ByteStream } from "../../util/stream";
interface RsaKey {
N: bigint;
d: bigint;
e: bigint;
hashN: number;
}
export interface ClientHello {
pcbId: string;
protocol: string;
model: string;
}
export interface IdzConnection {
aesStream: ByteStream;
clientHello: ClientHello;
}
// Proof-of-concept, so we only ever use one of the ten RSA key pairs
const rsaKey = {
N: 4922323266120814292574970172377860734034664704992758249880018618131907367614177800329506877981986877921220485681998287752778495334541127048495486311792061n,
d: 1163847742215766215216916151663017691387519688859977157498780867776436010396072628219119707788340687440419444081289736279466637153082223960965411473296473n,
e: 3961365081960959178294197133768419551060435043430437330799371731939550352626564261219865471710058480523874787120718634318364066605378505537556570049131337n,
hashN: 2662304617,
};
// Proof-of-concept, so we only use one fixed session key
const aesKey = Buffer.from("ffddeeccbbaa99887766554433221100", "hex");
function writeServerHello(aesKey: Buffer, rsaKey: RsaKey): Buffer {
const M = readBigInt(aesKey);
const keyEnc = modPow(M, rsaKey.e, rsaKey.N);
const result = Buffer.alloc(0x48);
result.set(writeBigInt(keyEnc, 0x40), 0x00);
result.writeUInt32LE(0x01020304, 0x40); // Meaning is unknown
result.writeUInt32LE(rsaKey.hashN, 0x44);
return result;
}
function readClientHello(buf: Buffer) {
const magic = buf.readUInt32LE(0x00);
if (magic === 0xFE78571D) {
throw new Error(
"Server Box data, ignore."
);
}
if (magic !== 0x01020304) {
throw new Error(
"Invalid magic number, cryptographic processing probably incorrect."
);
}
return {
pcbId: buf.slice(0x04, 0x0f).toString("ascii"),
protocol: buf.slice(0x10, 0x13).toString("ascii"),
model: buf.slice(0x18, 0x0c).toString("ascii"),
};
}
export default async function setup(socket: Socket): Promise<IdzConnection> {
const tcpStream = new IoByteStream(socket);
const serverHello = writeServerHello(aesKey, rsaKey);
await tcpStream.write(serverHello);
const aesStream = new AesEcbStream(tcpStream, aesKey);
const response = await aesStream.read(0x30);
if (response.length !== 0x30) {
throw new Error("Truncated client hello");
}
const clientHello = readClientHello(response);
return { aesStream, clientHello };
}

View File

@ -1,8 +0,0 @@
import { CheckTeamNameRequest } from "../request/checkTeamName";
checkTeamName.msgCode = 0x00a2;
checkTeamName.msgLen = 0x0040;
export function checkTeamName(buf: Buffer): CheckTeamNameRequest {
return { type: "check_team_name_req" };
}

View File

@ -1,14 +0,0 @@
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),
};
}

View File

@ -1,24 +0,0 @@
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)),
};
}

View File

@ -1,24 +0,0 @@
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"),
};
}

View File

@ -1,12 +0,0 @@
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,
};
}

View File

@ -1,206 +0,0 @@
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: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);
}
}

View File

@ -1,30 +0,0 @@
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>,
};
}

View File

@ -1,8 +0,0 @@
import { LoadConfigRequest } from "../request/loadConfig";
loadConfig.msgCode = 0x0004;
loadConfig.msgLen = 0x0050;
export function loadConfig(): LoadConfigRequest {
return { type: "load_config_req" };
}

View File

@ -1,8 +0,0 @@
import { LoadConfigRequest2 } from "../request/loadConfig2";
loadConfig2.msgCode = 0x00ab;
loadConfig2.msgLen = 0x0010;
export function loadConfig2(): LoadConfigRequest2 {
return { type: "load_config_v2_req" };
}

View File

@ -1,12 +0,0 @@
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,
};
}

View File

@ -1,12 +0,0 @@
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,
};
}

View File

@ -1,14 +0,0 @@
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),
};
}

View File

@ -1,15 +0,0 @@
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),
};
}

View File

@ -1,29 +0,0 @@
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"),
};
}

View File

@ -1,10 +0,0 @@
import { LoadRewardTableRequest } from "../request/loadRewardTable";
loadRewardTable.msgCode = 0x0086;
loadRewardTable.msgLen = 0x0010;
export function loadRewardTable(): LoadRewardTableRequest {
return {
type: "load_reward_table_req",
};
}

View File

@ -1,12 +0,0 @@
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,
};
}

View File

@ -1,16 +0,0 @@
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,
};
}

View File

@ -1,21 +0,0 @@
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",
};
}

View File

@ -1,11 +0,0 @@
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),
};
}

View File

@ -1,13 +0,0 @@
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),
};
}

View File

@ -1,13 +0,0 @@
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"),
};
}

View File

@ -1,8 +0,0 @@
import { Message00AD } from "../request/msg00AD";
msg00AD.msgCode = 0x00ad;
msg00AD.msgLen = 0x061c;
export function msg00AD(buf: Buffer): Message00AD {
return { type: "msg_00AD_req" };
}

View File

@ -1,22 +0,0 @@
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,
};
}

View File

@ -1,16 +0,0 @@
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),
};
}

View File

@ -1,23 +0,0 @@
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),
};
}

View File

@ -1,19 +0,0 @@
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)),
};
}

View File

@ -1,15 +0,0 @@
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),
};
}

View File

@ -1,13 +0,0 @@
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,
};
}

View File

@ -1,12 +0,0 @@
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"),
};
}

View File

@ -1,13 +0,0 @@
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),
};
}

View File

@ -1,28 +0,0 @@
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,
};
}

View File

@ -1,16 +0,0 @@
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"),
};
}

View File

@ -1,16 +0,0 @@
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>,
};
}

View File

@ -1,12 +0,0 @@
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),
};
}

View File

@ -1,12 +0,0 @@
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),
};
}

View File

@ -1,8 +0,0 @@
import { UpdateUserLogRequest } from "../request/updateUserLog";
updateUserLog.msgCode = 0x00bd;
updateUserLog.msgLen = 0x0050;
export function updateUserLog(buf: Buffer): UpdateUserLogRequest {
return { type: "update_user_log_req" };
}

View File

@ -1,18 +0,0 @@
import { Chara } from "../model/chara";
export function encodeChara(chara: Chara): Buffer {
const buf = Buffer.alloc(0x0014);
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); // Swapped on save
buf.writeUInt16LE(chara.title, 0x12); // Swapped on save
return buf;
}

View File

@ -1,59 +0,0 @@
import iconv from "iconv-lite";
import { CreateAutoTeamResponse } from "../response/createAutoTeam";
import { LoadTeamResponse } from "../response/loadTeam";
import { encodeChara } from "./_chara";
export function _team(res: CreateAutoTeamResponse | LoadTeamResponse) {
const buf = Buffer.alloc(0x0ca0);
if (res.type === "create_auto_team_res") {
buf.writeInt16LE(0x007c, 0x0000);
} else {
buf.writeInt16LE(0x0078, 0x0000);
}
const leader = res.members.find(item => item.leader === true);
buf.writeUInt32LE(res.team.extId, 0x000c);
iconv.encode(leader ? leader.profile.name : "Error\0", "shift_jis").copy(buf, 0x0010);
iconv.encode(res.team.name, "shift_jis").copy(buf, 0x0024);
iconv.encode(process.env.SHOP_NAME ? process.env.SHOP_NAME : "\0", "shift_jis").copy(buf, 0x0044);
buf.writeUInt32LE(res.team.nameBg, 0x00d8);
buf.writeUInt32LE(res.team.nameFx, 0x00dc);
buf.fill(0xff, 0x00e0, 0x00f9); // Bitset: Unlocked BGs probably
buf.fill(0xff, 0x00f9, 0x0101); // Bitset: Unlocked FX probably
buf.writeUInt32LE(leader ? leader.profile.aimeId : 0, 0x0080);
for (let i = 0; i < 6; i++) {
const base = 0x011c + i * 0x005c;
const member = res.members[i];
if (member === undefined) {
break;
}
const { profile, chara } = member;
const accessTime = (profile.accessTime.getTime() / 1000) | 0;
buf.writeInt32LE(profile.aimeId, base + 0x0000);
iconv.encode(profile.name + "\0", "shift_jis").copy(buf, base + 0x0004);
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);
}
// Team Time Attack:
/*for (let i = 0; i < 6; i++) {
const base = 0x0344 + 0x20 * i;
buf.writeInt16LE(0x00001, base + 0x0000);
buf.writeInt8(0x02, base + 0x0003);
buf.writeInt32LE(0x00000003, base + 0x0004);
iconv.encode("str\0", "shift_jis").copy(buf, base + 0x0008);
buf.writeInt32LE(0x00000004, base + 0x001c);
}*/
return buf;
}

View File

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

View File

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

View File

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

View File

@ -1,10 +0,0 @@
import { GenericResponse } from "../response/generic";
export function generic(res: GenericResponse) {
const buf = Buffer.alloc(0x0020);
buf.writeInt16LE(0x0001, 0x0000);
buf.writeInt32LE(res.status || 0, 0x0004);
return buf;
}

View File

@ -1,167 +0,0 @@
import logger from "debug";
import { Transform } from "stream";
import { _team } from "./_team";
import { checkTeamName } from "./checkTeamName";
import { createTeam } from "./createTeam";
import { discoverProfile } from "./discoverProfile";
import { generic } from "./generic";
import { lockProfile } from "./lockProfile";
import { lockProfileExtend } from "./lockProfileExtend";
import { load2on2 } from "./load2on2";
import { loadConfig } from "./loadConfig";
import { loadConfig2 } from "./loadConfig2";
import { loadEventInfo } from "./loadEventInfo";
import { loadGacha } from "./loadGacha";
import { loadGarage } from "./loadGarage";
import { loadGeneralReward } from "./loadGeneralReward";
import { loadGhost } from "./loadGhost";
import { loadProfile } from "./loadProfile";
import { loadRewardTable } from "./loadRewardTable";
import { loadServerList } from "./loadServerList";
import { loadStocker } from "./loadStocker";
import { loadTeamRanking } from "./loadTeamRanking";
import { loadTopTen } from "./loadTopTen";
import { saveExpedition } 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 { updateStoryClearNum } from "./updateStoryClearNum";
import { updateTeamLeader } from "./updateTeamLeader";
import { updateTeamMember } from "./updateTeamMember";
import { Response } from "../response";
const debug = logger("app:idz:encoder");
function encode(res: Response): Buffer {
switch (res.type) {
case "check_team_name_res":
return checkTeamName(res);
case "create_auto_team_res":
return _team(res);
case "create_team_res":
return createTeam(res);
case "discover_profile_res":
return discoverProfile(res);
case "generic_res":
return generic(res);
case "load_2on2_res":
return load2on2(res);
case "load_config_res":
return loadConfig(res);
case "load_config_v2_res":
return loadConfig2(res);
case "load_event_info_res":
return loadEventInfo(res);
case "load_gacha_res":
return loadGacha(res);
case "load_garage_res":
return loadGarage(res);
case "load_general_reward_res":
return loadGeneralReward(res);
case "load_ghost_res":
return loadGhost(res);
case "load_profile_res":
return loadProfile(res);
case "load_reward_table_res":
return loadRewardTable(res);
case "load_server_list_res":
return loadServerList(res);
case "load_stocker_res":
return loadStocker(res);
case "load_team_res":
return _team(res);
case "load_team_ranking_res":
return loadTeamRanking(res);
case "load_top_ten_res":
return loadTopTen(res);
case "lock_profile_extend_res":
return lockProfileExtend(res);
case "lock_profile_res":
return lockProfile(res);
case "save_expedition_res":
return saveExpedition(res);
case "save_garage_res":
return saveGarage(res);
case "save_new_car_res":
return saveNewCar(res);
case "save_time_attack_res":
return saveTimeAttack(res);
case "unlock_profile_res":
return unlockProfile(res);
case "update_provisional_store_rank_res":
return updateProvisionalStoreRank(res);
case "update_story_clear_num_res":
return updateStoryClearNum(res);
case "update_team_leader_res":
return updateTeamLeader(res);
case "update_team_member_res":
return updateTeamMember(res);
case "save_topic_res":
return saveTopic(res);
default:
const exhaustCheck: never = res;
throw new Error(`No writer fn for ${res["type"]}`);
}
}
export class Encoder extends Transform {
constructor() {
super({
readableObjectMode: true,
writableObjectMode: true,
});
}
_transform(res: Response, encoding, callback) {
debug("Object: %j", res);
const buf = encode(res);
if (debug.enabled) {
debug("Encoded: %s", buf.toString("hex"));
}
if (buf.readInt16LE(0) === 0) {
throw new Error("Missing message type code");
}
return callback(null, buf);
}
}

View File

@ -1,37 +0,0 @@
import {
Load2on2Response,
Load2on2Response1,
Load2on2Response2,
} from "../response/load2on2";
export function load2on2(res: Load2on2Response): Buffer {
switch (res.format) {
case 1:
return load2on2_v1(res);
case 2:
return load2on2_v2(res);
default:
const exhaust: never = res;
throw new Error(`Unsupported 2on2 response format ${res["format"]}`);
}
}
function load2on2_v1(res: Load2on2Response1): Buffer {
const buf = Buffer.alloc(0x04c0);
buf.writeInt16LE(0x00b1, 0x0000);
return buf;
}
// Same size but presumably incompatible somehow
function load2on2_v2(res: Load2on2Response2): Buffer {
const buf = Buffer.alloc(0x04c0);
buf.writeInt16LE(0x0133, 0x0000);
return buf;
}

View File

@ -1,11 +0,0 @@
import { LoadConfigResponse } from "../response/loadConfig";
export function loadConfig(res: LoadConfigResponse) {
const buf = Buffer.alloc(0x01a0);
buf.writeInt16LE(0x0005, 0x0000);
buf.writeInt8(res.status, 0x0002);
buf.writeUInt16LE(res.serverVersion, 0x0016);
return buf;
}

View File

@ -1,10 +0,0 @@
import { LoadConfigResponse2 } from "../response/loadConfig2";
export function loadConfig2(res: LoadConfigResponse2) {
const buf = Buffer.alloc(0x0230);
buf.writeInt16LE(0x00ac, 0x0000);
buf.writeInt8(res.status, 0x0002);
return buf;
}

View File

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

View File

@ -1,10 +0,0 @@
import { LoadGachaResponse } from "../response/loadGacha";
export function loadGacha(res: LoadGachaResponse): Buffer {
const buf = Buffer.alloc(0x0090);
buf.writeUInt16LE(0x00c2, 0x0000);
buf.writeUInt8(res.awardedToday ? 0x01 : 0x00, 0x0002);
return buf;
}

View File

@ -1,15 +0,0 @@
import { encodeCar } from "./_car";
import { LoadGarageResponse } from "../response/loadGarage";
export function loadGarage(res: LoadGarageResponse): Buffer {
const buf = Buffer.alloc(0x03d0);
buf.writeUInt16LE(0x0091, 0x0000);
buf.writeUInt16LE(res.cars.length, 0x0002);
for (let i = 0; i < res.cars.length; i++) {
encodeCar(res.cars[i]).copy(buf, 0x0004 + 0x0060 * i);
}
return buf;
}

View File

@ -1,22 +0,0 @@
import { loadProfile1 } from "./loadProfile1";
import { loadProfile2 } from "./loadProfile2";
import { loadProfile3 } from "./loadProfile3";
import { LoadProfileResponse } from "../response/loadProfile";
export function loadProfile(res: LoadProfileResponse) {
switch (res.format) {
case 1:
return loadProfile1(res);
case 2:
return loadProfile2(res);
case 3:
return loadProfile3(res);
default:
const exhaust: never = res;
throw new Error(`Unsupported profile response format ${res["format"]}`);
}
}

View File

@ -1,9 +0,0 @@
import { LoadRewardTableResponse } from "../response/loadRewardTable";
export function loadRewardTable(res: LoadRewardTableResponse) {
const buf = Buffer.alloc(0x01c0);
buf.writeInt16LE(0x0087, 0x0000);
return buf;
}

View File

@ -1,31 +0,0 @@
import { LoadServerListResponse } from "../response/loadServerList";
export function loadServerList(res: LoadServerListResponse) {
const buf = Buffer.alloc(0x04b0);
buf.writeInt16LE(0x0007, 0x0000);
buf.writeInt16LE(res.status, 0x0002);
buf.write(res.userDb.addr, 0x0004);
buf.writeInt16LE(res.userDb.tcp, 0x0084);
buf.writeInt16LE(res.userDb.http, 0x0086);
buf.write(res.matchAddr, 0x0088);
buf.writeInt16LE(res.matchPort.tcp, 0x0108);
buf.writeInt16LE(res.matchPort.udpSend, 0x010a);
buf.writeInt16LE(res.matchPort.udpRecv, 0x010c);
buf.writeInt16LE(res.tagMatchPort.tcp, 0x010e);
buf.writeInt16LE(res.tagMatchPort.udpSend, 0x0110);
buf.writeInt16LE(res.tagMatchPort.udpRecv, 0x0112);
buf.write(res.event.addr, 0x0114);
buf.writeInt16LE(res.event.tcp, 0x0194);
buf.write(res.screenshot.addr, 0x0198);
buf.writeInt16LE(res.screenshot.tcp, 0x0218);
buf.write(res.pingReturn, 0x021c);
buf.write(res.echo1.addr, 0x029c);
buf.write(res.echo2.addr, 0x031c);
buf.writeInt16LE(res.echo1.udp, 0x39c);
buf.writeInt16LE(res.echo2.udp, 0x39e);
buf.write(res.newsUrl, 0x03a0);
buf.write(res.reportErrorUrl, 0x0424);
return buf;
}

View File

@ -1,12 +0,0 @@
import { encodeBitmap } from "./_bitmap";
import { LoadStockerResponse } from "../response/loadStocker";
export function loadStocker(res: LoadStockerResponse) {
const buf = Buffer.alloc(0x00a0);
buf.writeInt16LE(0x00a8, 0x0000);
buf.writeUInt8(res.status, 0x0002);
encodeBitmap(res.backgrounds, 0x24).copy(buf, 0x0003);
return buf;
}

View File

@ -1,10 +0,0 @@
import { LoadTeamRankingResponse } from "../response/loadTeamRanking";
export function loadTeamRanking(res: LoadTeamRankingResponse): Buffer {
const buf = Buffer.alloc(0x0ba0);
buf.writeUInt16LE(0x00ba, 0x0000);
// Row stride is 0x94
return buf;
}

View File

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

View File

@ -1,38 +0,0 @@
import {
SaveExpeditionResponse,
SaveExpeditionResponse1,
SaveExpeditionResponse2,
} from "../response/saveExpedition";
export function saveExpedition(res: SaveExpeditionResponse): Buffer {
switch (res.format) {
case 1:
return saveExpedition1(res);
case 2:
return saveExpedition2(res);
default:
const exhaust: never = res;
throw new Error(`Unsupported data format ${res["format"]}`);
}
}
function saveExpedition1(res: SaveExpeditionResponse1): Buffer {
// in awe of the size of this lad
const buf = Buffer.alloc(0x17c0);
buf.writeInt16LE(0x008d, 0x0000);
return buf;
}
function saveExpedition2(res: SaveExpeditionResponse2): Buffer {
// absolute unit
const buf = Buffer.alloc(0x18ac);
buf.writeUInt16LE(0x0140, 0x0000);
return buf;
}

View File

@ -1,10 +0,0 @@
import { SaveGarageResponse } from "../response/saveGarage";
export function saveGarage(res: SaveGarageResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x008f, 0x0000);
buf.writeUInt16LE(res.status, 0x0002);
return buf;
}

View File

@ -1,10 +0,0 @@
import { SaveNewCarResponse } from "../response/saveNewCar";
export function saveNewCar(res: SaveNewCarResponse): Buffer {
const buf = Buffer.alloc(0x0010);
buf.writeUInt16LE(0x007a, 0x0000);
buf.writeUInt8(res.status, 0x0002);
return buf;
}

View File

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

View File

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

View File

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

View File

@ -1,25 +0,0 @@
import iconv from "iconv-lite";
import { UpdateProvisionalStoreRankResponse } from "../response/updateProvisionalStoreRank";
export function updateProvisionalStoreRank(
res: UpdateProvisionalStoreRankResponse
) {
const buf = Buffer.alloc(0x02b0);
buf.writeInt16LE(0x0083, 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);
iconv.encode(row.field_0010, "shift_jis").copy(buf, offset + 0x0010);
iconv.encode(row.field_003B, "shift_jis").copy(buf, offset + 0x003b);
}
}
return buf;
}

View File

@ -1,36 +0,0 @@
import {
UpdateStoryClearNumResponse,
UpdateStoryClearNumResponse1,
UpdateStoryClearNumResponse2,
} from "../response/updateStoryClearNum";
export function updateStoryClearNum(res: UpdateStoryClearNumResponse): Buffer {
switch (res.format) {
case 1:
return updateStoryClearNum1(res);
case 2:
return updateStoryClearNum2(res);
default:
const exhaust: never = res;
throw new Error(`Unsupported data format ${res["format"]}`);
}
}
function updateStoryClearNum1(res: UpdateStoryClearNumResponse1): Buffer {
const buf = Buffer.alloc(0x0220);
buf.writeInt16LE(0x0080, 0x0000);
return buf;
}
function updateStoryClearNum2(res: UpdateStoryClearNumResponse2): Buffer {
const buf = Buffer.alloc(0x04f0);
buf.writeInt16LE(0x013e, 0x0000);
return buf;
}

View File

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

View File

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

View File

@ -1,13 +0,0 @@
import { Load2on2Request } from "../request/load2on2";
import { Load2on2Response } from "../response/load2on2";
import { Repositories } from "../repo";
export function load2on2(
w: Repositories,
req: Load2on2Request
): Load2on2Response {
return {
type: "load_2on2_res",
format: req.format as any,
};
}

View File

@ -1,14 +0,0 @@
import { LoadConfigRequest } from "../request/loadConfig";
import { LoadConfigResponse } from "../response/loadConfig";
import { Repositories } from "../repo";
export function loadConfig(
w: Repositories,
req: LoadConfigRequest
): LoadConfigResponse {
return {
type: "load_config_res",
status: 1,
serverVersion: 130,
};
}

View File

@ -1,13 +0,0 @@
import { LoadConfigRequest2 } from "../request/loadConfig2";
import { LoadConfigResponse2 } from "../response/loadConfig2";
import { Repositories } from "../repo";
export function loadConfig2(
w: Repositories,
req: LoadConfigRequest2
): LoadConfigResponse2 {
return {
type: "load_config_v2_res",
status: 1,
};
}

View File

@ -1,7 +0,0 @@
import { Message00AD } from "../request/msg00AD";
import { GenericResponse } from "../response/generic";
import { Repositories } from "../repo";
export function msg00AD(w: Repositories, req: Message00AD): GenericResponse {
return { type: "generic_res" };
}

View File

@ -1,35 +0,0 @@
import logger from "debug";
import { Socket } from "net";
import { dispatch } from "./handler";
import { setup } from "./setup";
import { DataSource } from "../sql";
import { SqlRepositories } from "./sql";
const debug = logger("app:idz:session");
export default function idz(db: DataSource) {
return async function(socket: Socket) {
const { input, output } = setup(socket);
debug("Connection opened");
try {
for await (const req of input) {
const res = await db.transaction(txn =>
dispatch(new SqlRepositories(txn), req)
);
output.write(res);
}
} catch (e) {
if (debug.enabled) {
debug("Error: %s", e.stack);
}
}
debug("Connection closed");
input.end();
};
}

View File

@ -1,20 +0,0 @@
import { ExtId } from "../model/base";
import { Team } from "../model/team";
import { AimeId } from "../../model";
interface Load2on2RequestBase {
type: "load_2on2_req";
field_0002: number;
aimeId: AimeId;
teamId: ExtId<Team>;
}
export interface Load2on2Request1 extends Load2on2RequestBase {
format: 1;
}
export interface Load2on2Request2 extends Load2on2RequestBase {
format: 2;
}
export type Load2on2Request = Load2on2Request1 | Load2on2Request2;

View File

@ -1,3 +0,0 @@
export interface LoadConfigRequest {
type: "load_config_req";
}

View File

@ -1,3 +0,0 @@
export interface LoadConfigRequest2 {
type: "load_config_v2_req";
}

View File

@ -1,20 +0,0 @@
import { AimeId } from "../../model";
interface LoadGeneralRewardRequestBase {
type: "load_general_reward_req";
aimeId: AimeId;
}
export interface LoadGeneralRewardRequest1
extends LoadGeneralRewardRequestBase {
format: 1;
}
export interface LoadGeneralRewardRequest2
extends LoadGeneralRewardRequestBase {
format: 2;
}
export type LoadGeneralRewardRequest =
| LoadGeneralRewardRequest1
| LoadGeneralRewardRequest2;

View File

@ -1,17 +0,0 @@
import { AimeId } from "../../model";
interface LoadProfileRequestBase {
type: "load_profile_req";
aimeId: AimeId;
luid: string;
}
export interface LoadProfileRequest2 extends LoadProfileRequestBase {
format: 2;
}
export interface LoadProfileRequest3 extends LoadProfileRequestBase {
format: 3;
}
export type LoadProfileRequest = LoadProfileRequest2 | LoadProfileRequest3;

View File

@ -1,3 +0,0 @@
export interface Message00AD {
type: "msg_00AD_req";
}

View File

@ -1,16 +0,0 @@
interface SaveExpeditionRequestBase {
type: "save_expedition_req";
field_0004: number;
}
export interface SaveExpeditionRequest1 extends SaveExpeditionRequestBase {
format: 1;
}
export interface SaveExpeditionRequest2 extends SaveExpeditionRequestBase {
format: 2;
}
export type SaveExpeditionRequest =
| SaveExpeditionRequest1
| SaveExpeditionRequest2;

View File

@ -1,12 +0,0 @@
import { BackgroundCode } from "../model/base";
import { CarSelector } from "../model/car";
import { Chara } from "../model/chara";
import { AimeId } from "../../model";
export interface SaveStockerRequest {
type: "save_stocker_req";
aimeId: AimeId;
selectedCar: CarSelector;
backgrounds: Set<BackgroundCode>;
chara: Chara;
}

View File

@ -1,17 +0,0 @@
interface UpdateStoryClearNumRequestBase {
type: "update_story_clear_num_req";
}
export interface UpdateStoryClearNumRequest1
extends UpdateStoryClearNumRequestBase {
format: 1;
}
export interface UpdateStoryClearNumRequest2
extends UpdateStoryClearNumRequestBase {
format: 2;
}
export type UpdateStoryClearNumRequest =
| UpdateStoryClearNumRequest1
| UpdateStoryClearNumRequest2;

View File

@ -1,14 +0,0 @@
interface Load2on2ResponseBase {
type: "load_2on2_res";
// TODO?
}
export interface Load2on2Response1 extends Load2on2ResponseBase {
format: 1;
}
export interface Load2on2Response2 extends Load2on2ResponseBase {
format: 2;
}
export type Load2on2Response = Load2on2Response1 | Load2on2Response2;

View File

@ -1,5 +0,0 @@
export interface LoadConfigResponse {
type: "load_config_res";
status: number;
serverVersion: number;
}

View File

@ -1,4 +0,0 @@
export interface LoadConfigResponse2 {
type: "load_config_v2_res";
status: number;
}

Some files were not shown because too many files have changed in this diff Show More