Hackily get the client to a channel server

This commit is contained in:
Andrew Gutekanst
2019-12-24 00:20:35 +09:00
parent 7aef17f7d9
commit e5066d4f8b
13 changed files with 1275 additions and 27 deletions

View File

@@ -2,4 +2,14 @@
Nothing here yet :)
Based on the TW version. Requires a local mirror of the original launcher site to be placed in `./www/g6_launcher` until I can RE the launcher and figure out which JS callbacks it requires.
Based on the TW version. Requires a local mirror of the original launcher site to be placed in `./www/g6_launcher` until I can RE the launcher and figure out which JS callbacks it requires.
## Installation
Clone the repo
Install PostgreSQL, launch psql shell, `CREATE DATABASE erupe;`.
Setup db with golang-migrate:
`go get -tags 'postgres' -u github.com/golang-migrate/migrate/cmd/migrate`
`set POSTGRESQL_URL=postgres://postgres:password@localhost:5432/erupe?sslmode=disable`
`migrate -database %POSTGRESQL_URL% -path migrations up`

46
channel_server.go Normal file
View File

@@ -0,0 +1,46 @@
package main
import (
"encoding/hex"
"fmt"
"net"
"github.com/Andoryuuta/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
func handleChannelServerConnection(conn net.Conn) {
fmt.Println("Channel server got connection!")
// Unlike the sign and entrance server,
// the client DOES NOT initalize the channel connection with 8 NULL bytes.
cc := network.NewCryptConn(conn)
for {
pkt, err := cc.ReadPacket()
if err != nil {
return
}
bf := byteframe.NewByteFrameFromBytes(pkt)
opcode := network.PacketID(bf.ReadUint16())
fmt.Printf("Opcode: %s\n", opcode)
fmt.Printf("Data:\n%s\n", hex.Dump(pkt))
}
}
func doChannelServer(listenAddr string) {
l, err := net.Listen("tcp", listenAddr)
if err != nil {
panic(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
panic(err)
}
go handleChannelServerConnection(conn)
}
}

25
config/config.go Normal file
View File

@@ -0,0 +1,25 @@
package config
import "github.com/BurntSushi/toml"
// Config holds the configuration settings from the toml file.
type Config struct {
DB Database `toml:"database"`
}
// Database holds the postgres database config.
type Database struct {
Server string
Port int
}
// LoadConfig loads the given config toml file.
func LoadConfig(filepath string) (*Config, error) {
c := &Config{}
_, err := toml.DecodeFile(filepath, c)
if err != nil {
return nil, err
}
return c, nil
}

View File

@@ -31,7 +31,7 @@ func handleEntranceServerConnection(conn net.Conn) {
fmt.Printf("Got entrance server command:\n%s\n", hex.Dump(pkt))
data, err := ioutil.ReadFile("tw_server_list_resp.bin")
data, err := ioutil.ReadFile("custom_entrance_server_resp.bin")//("tw_server_list_resp.bin")
if err != nil {
print(err)
return

2
go.mod
View File

@@ -5,5 +5,7 @@ go 1.13
require (
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0
github.com/BurntSushi/toml v0.3.1
github.com/golang-migrate/migrate v3.5.4+incompatible // indirect
github.com/julienschmidt/httprouter v1.3.0
)

4
go.sum
View File

@@ -2,5 +2,9 @@ github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3 h1:N8pCiqpJAHyOO8
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3/go.mod h1:4WK1jUpH8NFdDiv7IJcBfyCIOMqKjZ15kcw5eBKALvc=
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 h1:2pVgen9rh18IxSWxOa80bObcpyfrS6d5bJtZeCUN7rY=
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=

View File

@@ -11,6 +11,7 @@ func main() {
go serveLauncherHTML(":80")
go doEntranceServer(":53310")
go doSignServer(":53312")
go doChannelServer(":54001")
for {
time.Sleep(1 * time.Second)

View File

@@ -0,0 +1,10 @@
BEGIN;
DROP TABLE IF EXISTS sign_sessions;
DROP TABLE IF EXISTS characters;
DROP TABLE IF EXISTS users;
DROP DOMAIN IF EXISTS uint8;
DROP DOMAIN IF EXISTS uint16;
END;

View File

@@ -0,0 +1,36 @@
BEGIN;
CREATE DOMAIN uint8 AS smallint
CHECK(VALUE >= 0 AND VALUE <= 255);
CREATE DOMAIN uint16 AS integer
CHECK(VALUE >= 0 AND VALUE <= 65536);
CREATE TABLE users (
id serial NOT NULL PRIMARY KEY,
username text UNIQUE NOT NULL,
password text NOT NULL
);
CREATE TABLE characters (
id serial NOT NULL PRIMARY KEY,
user_id bigint REFERENCES users(id),
is_female boolean,
is_new_character boolean,
small_gr_level uint8,
gr_override_mode boolean,
name varchar(15),
unk_desc_string varchar(31),
gr_override_level uint16,
gr_override_unk0 uint8,
gr_override_unk1 uint8
);
CREATE TABLE sign_sessions (
id serial NOT NULL PRIMARY KEY,
user_id bigint REFERENCES users(id),
auth_token_num bigint,
auth_token_str text
);
END;

442
network/packetid.go Normal file
View File

@@ -0,0 +1,442 @@
package network
//revive:disable
type PacketID uint16
//go:generate stringer -type=PacketID
const (
MSG_HEAD PacketID = iota
MSG_SYS_reserve01
MSG_SYS_reserve02
MSG_SYS_reserve03
MSG_SYS_reserve04
MSG_SYS_reserve05
MSG_SYS_reserve06
MSG_SYS_reserve07
MSG_SYS_ADD_OBJECT
MSG_SYS_DEL_OBJECT
MSG_SYS_DISP_OBJECT
MSG_SYS_HIDE_OBJECT
MSG_SYS_reserve0C
MSG_SYS_reserve0D
MSG_SYS_reserve0E
MSG_SYS_EXTEND_THRESHOLD
MSG_SYS_END
MSG_SYS_NOP
MSG_SYS_ACK
MSG_SYS_TERMINAL_LOG
MSG_SYS_LOGIN
MSG_SYS_LOGOUT
MSG_SYS_SET_STATUS
MSG_SYS_PING
MSG_SYS_CAST_BINARY
MSG_SYS_HIDE_CLIENT
MSG_SYS_TIME
MSG_SYS_CASTED_BINARY
MSG_SYS_GET_FILE
MSG_SYS_ISSUE_LOGKEY
MSG_SYS_RECORD_LOG
MSG_SYS_ECHO
MSG_SYS_CREATE_STAGE
MSG_SYS_STAGE_DESTRUCT
MSG_SYS_ENTER_STAGE
MSG_SYS_BACK_STAGE
MSG_SYS_MOVE_STAGE
MSG_SYS_LEAVE_STAGE
MSG_SYS_LOCK_STAGE
MSG_SYS_UNLOCK_STAGE
MSG_SYS_RESERVE_STAGE
MSG_SYS_UNRESERVE_STAGE
MSG_SYS_SET_STAGE_PASS
MSG_SYS_WAIT_STAGE_BINARY
MSG_SYS_SET_STAGE_BINARY
MSG_SYS_GET_STAGE_BINARY
MSG_SYS_ENUMERATE_CLIENT
MSG_SYS_ENUMERATE_STAGE
MSG_SYS_CREATE_MUTEX
MSG_SYS_CREATE_OPEN_MUTEX
MSG_SYS_DELETE_MUTEX
MSG_SYS_OPEN_MUTEX
MSG_SYS_CLOSE_MUTEX
MSG_SYS_CREATE_SEMAPHORE
MSG_SYS_CREATE_ACQUIRE_SEMAPHORE
MSG_SYS_DELETE_SEMAPHORE
MSG_SYS_ACQUIRE_SEMAPHORE
MSG_SYS_RELEASE_SEMAPHORE
MSG_SYS_LOCK_GLOBAL_SEMA
MSG_SYS_UNLOCK_GLOBAL_SEMA
MSG_SYS_CHECK_SEMAPHORE
MSG_SYS_OPERATE_REGISTER
MSG_SYS_LOAD_REGISTER
MSG_SYS_NOTIFY_REGISTER
MSG_SYS_CREATE_OBJECT
MSG_SYS_DELETE_OBJECT
MSG_SYS_POSITION_OBJECT
MSG_SYS_ROTATE_OBJECT
MSG_SYS_DUPLICATE_OBJECT
MSG_SYS_SET_OBJECT_BINARY
MSG_SYS_GET_OBJECT_BINARY
MSG_SYS_GET_OBJECT_OWNER
MSG_SYS_UPDATE_OBJECT_BINARY
MSG_SYS_CLEANUP_OBJECT
MSG_SYS_reserve4A
MSG_SYS_reserve4B
MSG_SYS_reserve4C
MSG_SYS_reserve4D
MSG_SYS_reserve4E
MSG_SYS_reserve4F
MSG_SYS_INSERT_USER
MSG_SYS_DELETE_USER
MSG_SYS_SET_USER_BINARY
MSG_SYS_GET_USER_BINARY
MSG_SYS_NOTIFY_USER_BINARY
MSG_SYS_reserve55
MSG_SYS_reserve56
MSG_SYS_reserve57
MSG_SYS_UPDATE_RIGHT
MSG_SYS_AUTH_QUERY
MSG_SYS_AUTH_DATA
MSG_SYS_AUTH_TERMINAL
MSG_SYS_reserve5C
MSG_SYS_RIGHTS_RELOAD
MSG_SYS_reserve5E
MSG_SYS_reserve5F
MSG_MHF_SAVEDATA
MSG_MHF_LOADDATA
MSG_MHF_LIST_MEMBER
MSG_MHF_OPR_MEMBER
MSG_MHF_ENUMERATE_DIST_ITEM
MSG_MHF_APPLY_DIST_ITEM
MSG_MHF_ACQUIRE_DIST_ITEM
MSG_MHF_GET_DIST_DESCRIPTION
MSG_MHF_SEND_MAIL
MSG_MHF_READ_MAIL
MSG_MHF_LIST_MAIL
MSG_MHF_OPRT_MAIL
MSG_MHF_LOAD_FAVORITE_QUEST
MSG_MHF_SAVE_FAVORITE_QUEST
MSG_MHF_REGISTER_EVENT
MSG_MHF_RELEASE_EVENT
MSG_MHF_TRANSIT_MESSAGE
MSG_SYS_reserve71
MSG_SYS_reserve72
MSG_SYS_reserve73
MSG_SYS_reserve74
MSG_SYS_reserve75
MSG_SYS_reserve76
MSG_SYS_reserve77
MSG_SYS_reserve78
MSG_SYS_reserve79
MSG_SYS_reserve7A
MSG_SYS_reserve7B
MSG_SYS_reserve7C
MSG_CA_EXCHANGE_ITEM
MSG_SYS_reserve7E
MSG_MHF_PRESENT_BOX
MSG_MHF_SERVER_COMMAND
MSG_MHF_SHUT_CLIENT
MSG_MHF_ANNOUNCE
MSG_MHF_SET_LOGINWINDOW
MSG_SYS_TRANS_BINARY
MSG_SYS_COLLECT_BINARY
MSG_SYS_GET_STATE
MSG_SYS_SERIALIZE
MSG_SYS_ENUMLOBBY
MSG_SYS_ENUMUSER
MSG_SYS_INFOKYSERVER
MSG_MHF_GET_CA_UNIQUE_ID
MSG_MHF_SET_CA_ACHIEVEMENT
MSG_MHF_CARAVAN_MY_SCORE
MSG_MHF_CARAVAN_RANKING
MSG_MHF_CARAVAN_MY_RANK
MSG_MHF_CREATE_GUILD
MSG_MHF_OPERATE_GUILD
MSG_MHF_OPERATE_GUILD_MEMBER
MSG_MHF_INFO_GUILD
MSG_MHF_ENUMERATE_GUILD
MSG_MHF_UPDATE_GUILD
MSG_MHF_ARRANGE_GUILD_MEMBER
MSG_MHF_ENUMERATE_GUILD_MEMBER
MSG_MHF_ENUMERATE_CAMPAIGN
MSG_MHF_STATE_CAMPAIGN
MSG_MHF_APPLY_CAMPAIGN
MSG_MHF_ENUMERATE_ITEM
MSG_MHF_ACQUIRE_ITEM
MSG_MHF_TRANSFER_ITEM
MSG_MHF_MERCENARY_HUNTDATA
MSG_MHF_ENTRY_ROOKIE_GUILD
MSG_MHF_ENUMERATE_QUEST
MSG_MHF_ENUMERATE_EVENT
MSG_MHF_ENUMERATE_PRICE
MSG_MHF_ENUMERATE_RANKING
MSG_MHF_ENUMERATE_ORDER
MSG_MHF_ENUMERATE_SHOP
MSG_MHF_GET_EXTRA_INFO
MSG_MHF_UPDATE_INTERIOR
MSG_MHF_ENUMERATE_HOUSE
MSG_MHF_UPDATE_HOUSE
MSG_MHF_LOAD_HOUSE
MSG_MHF_OPERATE_WAREHOUSE
MSG_MHF_ENUMERATE_WAREHOUSE
MSG_MHF_UPDATE_WAREHOUSE
MSG_MHF_ACQUIRE_TITLE
MSG_MHF_ENUMERATE_TITLE
MSG_MHF_ENUMERATE_GUILD_ITEM
MSG_MHF_UPDATE_GUILD_ITEM
MSG_MHF_ENUMERATE_UNION_ITEM
MSG_MHF_UPDATE_UNION_ITEM
MSG_MHF_CREATE_JOINT
MSG_MHF_OPERATE_JOINT
MSG_MHF_INFO_JOINT
MSG_MHF_UPDATE_GUILD_ICON
MSG_MHF_INFO_FESTA
MSG_MHF_ENTRY_FESTA
MSG_MHF_CHARGE_FESTA
MSG_MHF_ACQUIRE_FESTA
MSG_MHF_STATE_FESTA_U
MSG_MHF_STATE_FESTA_G
MSG_MHF_ENUMERATE_FESTA_MEMBER
MSG_MHF_VOTE_FESTA
MSG_MHF_ACQUIRE_CAFE_ITEM
MSG_MHF_UPDATE_CAFEPOINT
MSG_MHF_CHECK_DAILY_CAFEPOINT
MSG_MHF_GET_COG_INFO
MSG_MHF_CHECK_MONTHLY_ITEM
MSG_MHF_ACQUIRE_MONTHLY_ITEM
MSG_MHF_CHECK_WEEKLY_STAMP
MSG_MHF_EXCHANGE_WEEKLY_STAMP
MSG_MHF_CREATE_MERCENARY
MSG_MHF_SAVE_MERCENARY
MSG_MHF_READ_MERCENARY_W
MSG_MHF_READ_MERCENARY_M
MSG_MHF_CONTRACT_MERCENARY
MSG_MHF_ENUMERATE_MERCENARY_LOG
MSG_MHF_ENUMERATE_GUACOT
MSG_MHF_UPDATE_GUACOT
MSG_MHF_INFO_TOURNAMENT
MSG_MHF_ENTRY_TOURNAMENT
MSG_MHF_ENTER_TOURNAMENT_QUEST
MSG_MHF_ACQUIRE_TOURNAMENT
MSG_MHF_GET_ACHIEVEMENT
MSG_MHF_RESET_ACHIEVEMENT
MSG_MHF_ADD_ACHIEVEMENT
MSG_MHF_PAYMENT_ACHIEVEMENT
MSG_MHF_DISPLAYED_ACHIEVEMENT
MSG_MHF_INFO_SCENARIO_COUNTER
MSG_MHF_SAVE_SCENARIO_DATA
MSG_MHF_LOAD_SCENARIO_DATA
MSG_MHF_GET_BBS_SNS_STATUS
MSG_MHF_APPLY_BBS_ARTICLE
MSG_MHF_GET_ETC_POINTS
MSG_MHF_UPDATE_ETC_POINT
MSG_MHF_GET_MYHOUSE_INFO
MSG_MHF_UPDATE_MYHOUSE_INFO
MSG_MHF_GET_WEEKLY_SCHEDULE
MSG_MHF_ENUMERATE_INV_GUILD
MSG_MHF_OPERATION_INV_GUILD
MSG_MHF_STAMPCARD_STAMP
MSG_MHF_STAMPCARD_PRIZE
MSG_MHF_UNRESERVE_SRG
MSG_MHF_LOAD_PLATE_DATA
MSG_MHF_SAVE_PLATE_DATA
MSG_MHF_LOAD_PLATE_BOX
MSG_MHF_SAVE_PLATE_BOX
MSG_MHF_READ_GUILDCARD
MSG_MHF_UPDATE_GUILDCARD
MSG_MHF_READ_BEAT_LEVEL
MSG_MHF_UPDATE_BEAT_LEVEL
MSG_MHF_READ_BEAT_LEVEL_ALL_RANKING
MSG_MHF_READ_BEAT_LEVEL_MY_RANKING
MSG_MHF_READ_LAST_WEEK_BEAT_RANKING
MSG_MHF_ACCEPT_READ_REWARD
MSG_MHF_GET_ADDITIONAL_BEAT_REWARD
MSG_MHF_GET_FIXED_SEIBATU_RANKING_TABLE
MSG_MHF_GET_BBS_USER_STATUS
MSG_MHF_KICK_EXPORT_FORCE
MSG_MHF_GET_BREAK_SEIBATU_LEVEL_REWARD
MSG_MHF_GET_WEEKLY_SEIBATU_RANKING_REWARD
MSG_MHF_GET_EARTH_STATUS
MSG_MHF_LOAD_PARTNER
MSG_MHF_SAVE_PARTNER
MSG_MHF_GET_GUILD_MISSION_LIST
MSG_MHF_GET_GUILD_MISSION_RECORD
MSG_MHF_ADD_GUILD_MISSION_COUNT
MSG_MHF_SET_GUILD_MISSION_TARGET
MSG_MHF_CANCEL_GUILD_MISSION_TARGET
MSG_MHF_LOAD_OTOMO_AIROU
MSG_MHF_SAVE_OTOMO_AIROU
MSG_MHF_ENUMERATE_GUILD_TRESURE
MSG_MHF_ENUMERATE_AIROULIST
MSG_MHF_REGIST_GUILD_TRESURE
MSG_MHF_ACQUIRE_GUILD_TRESURE
MSG_MHF_OPERATE_GUILD_TRESURE_REPORT
MSG_MHF_GET_GUILD_TRESURE_SOUVENIR
MSG_MHF_ACQUIRE_GUILD_TRESURE_SOUVENIR
MSG_MHF_ENUMERATE_FESTA_INTERMEDIATE_PRIZE
MSG_MHF_ACQUIRE_FESTA_INTERMEDIATE_PRIZE
MSG_MHF_LOAD_DECO_MYSET
MSG_MHF_SAVE_DECO_MYSET
MSG_MHF_reserve010F
MSG_MHF_LOAD_GUILD_COOKING
MSG_MHF_REGIST_GUILD_COOKING
MSG_MHF_LOAD_GUILD_ADVENTURE
MSG_MHF_REGIST_GUILD_ADVENTURE
MSG_MHF_ACQUIRE_GUILD_ADVENTURE
MSG_MHF_CHARGE_GUILD_ADVENTURE
MSG_MHF_LOAD_LEGEND_DISPATCH
MSG_MHF_LOAD_HUNTER_NAVI
MSG_MHF_SAVE_HUNTER_NAVI
MSG_MHF_REGIST_SPABI_TIME
MSG_MHF_GET_GUILD_WEEKLY_BONUS_MASTER
MSG_MHF_GET_GUILD_WEEKLY_BONUS_ACTIVE_COUNT
MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER
MSG_MHF_GET_TOWER_INFO
MSG_MHF_POST_TOWER_INFO
MSG_MHF_GET_GEM_INFO
MSG_MHF_POST_GEM_INFO
MSG_MHF_GET_EARTH_VALUE
MSG_MHF_DEBUG_POST_VALUE
MSG_MHF_GET_PAPER_DATA
MSG_MHF_GET_NOTICE
MSG_MHF_POST_NOTICE
MSG_MHF_GET_BOOST_TIME
MSG_MHF_POST_BOOST_TIME
MSG_MHF_GET_BOOST_TIME_LIMIT
MSG_MHF_POST_BOOST_TIME_LIMIT
MSG_MHF_ENUMERATE_FESTA_PERSONAL_PRIZE
MSG_MHF_ACQUIRE_FESTA_PERSONAL_PRIZE
MSG_MHF_GET_RAND_FROM_TABLE
MSG_MHF_GET_CAFE_DURATION
MSG_MHF_GET_CAFE_DURATION_BONUS_INFO
MSG_MHF_RECEIVE_CAFE_DURATION_BONUS
MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED
MSG_MHF_GET_GACHA_POINT
MSG_MHF_USE_GACHA_POINT
MSG_MHF_EXCHANGE_FPOINT_2_ITEM
MSG_MHF_EXCHANGE_ITEM_2_FPOINT
MSG_MHF_GET_FPOINT_EXCHANGE_LIST
MSG_MHF_PLAY_STEPUP_GACHA
MSG_MHF_RECEIVE_GACHA_ITEM
MSG_MHF_GET_STEPUP_STATUS
MSG_MHF_PLAY_FREE_GACHA
MSG_MHF_GET_TINY_BIN
MSG_MHF_POST_TINY_BIN
MSG_MHF_GET_SENYU_DAILY_COUNT
MSG_MHF_GET_GUILD_TARGET_MEMBER_NUM
MSG_MHF_GET_BOOST_RIGHT
MSG_MHF_START_BOOST_TIME
MSG_MHF_POST_BOOST_TIME_QUEST_RETURN
MSG_MHF_GET_BOX_GACHA_INFO
MSG_MHF_PLAY_BOX_GACHA
MSG_MHF_RESET_BOX_GACHA_INFO
MSG_MHF_GET_SEIBATTLE
MSG_MHF_POST_SEIBATTLE
MSG_MHF_GET_RYOUDAMA
MSG_MHF_POST_RYOUDAMA
MSG_MHF_GET_TENROUIRAI
MSG_MHF_POST_TENROUIRAI
MSG_MHF_POST_GUILD_SCOUT
MSG_MHF_CANCEL_GUILD_SCOUT
MSG_MHF_ANSWER_GUILD_SCOUT
MSG_MHF_GET_GUILD_SCOUT_LIST
MSG_MHF_GET_GUILD_MANAGE_RIGHT
MSG_MHF_SET_GUILD_MANAGE_RIGHT
MSG_MHF_PLAY_NORMAL_GACHA
MSG_MHF_GET_DAILY_MISSION_MASTER
MSG_MHF_GET_DAILY_MISSION_PERSONAL
MSG_MHF_SET_DAILY_MISSION_PERSONAL
MSG_MHF_GET_GACHA_PLAY_HISTORY
MSG_MHF_GET_REJECT_GUILD_SCOUT
MSG_MHF_SET_REJECT_GUILD_SCOUT
MSG_MHF_GET_CA_ACHIEVEMENT_HIST
MSG_MHF_SET_CA_ACHIEVEMENT_HIST
MSG_MHF_GET_KEEP_LOGIN_BOOST_STATUS
MSG_MHF_USE_KEEP_LOGIN_BOOST
MSG_MHF_GET_UD_SCHEDULE
MSG_MHF_GET_UD_INFO
MSG_MHF_GET_KIJU_INFO
MSG_MHF_SET_KIJU
MSG_MHF_ADD_UD_POINT
MSG_MHF_GET_UD_MY_POINT
MSG_MHF_GET_UD_TOTAL_POINT_INFO
MSG_MHF_GET_UD_BONUS_QUEST_INFO
MSG_MHF_GET_UD_SELECTED_COLOR_INFO
MSG_MHF_GET_UD_MONSTER_POINT
MSG_MHF_GET_UD_DAILY_PRESENT_LIST
MSG_MHF_GET_UD_NORMA_PRESENT_LIST
MSG_MHF_GET_UD_RANKING_REWARD_LIST
MSG_MHF_ACQUIRE_UD_ITEM
MSG_MHF_GET_REWARD_SONG
MSG_MHF_USE_REWARD_SONG
MSG_MHF_ADD_REWARD_SONG_COUNT
MSG_MHF_GET_UD_RANKING
MSG_MHF_GET_UD_MY_RANKING
MSG_MHF_ACQUIRE_MONTHLY_REWARD
MSG_MHF_GET_UD_GUILD_MAP_INFO
MSG_MHF_GENERATE_UD_GUILD_MAP
MSG_MHF_GET_UD_TACTICS_POINT
MSG_MHF_ADD_UD_TACTICS_POINT
MSG_MHF_GET_UD_TACTICS_RANKING
MSG_MHF_GET_UD_TACTICS_REWARD_LIST
MSG_MHF_GET_UD_TACTICS_LOG
MSG_MHF_GET_EQUIP_SKIN_HIST
MSG_MHF_UPDATE_EQUIP_SKIN_HIST
MSG_MHF_GET_UD_TACTICS_FOLLOWER
MSG_MHF_SET_UD_TACTICS_FOLLOWER
MSG_MHF_GET_UD_SHOP_COIN
MSG_MHF_USE_UD_SHOP_COIN
MSG_MHF_GET_ENHANCED_MINIDATA
MSG_MHF_SET_ENHANCED_MINIDATA
MSG_MHF_SEX_CHANGER
MSG_MHF_GET_LOBBY_CROWD
MSG_SYS_reserve180
MSG_MHF_GUILD_HUNTDATA
MSG_MHF_ADD_KOURYOU_POINT
MSG_MHF_GET_KOURYOU_POINT
MSG_MHF_EXCHANGE_KOURYOU_POINT
MSG_MHF_GET_UD_TACTICS_BONUS_QUEST
MSG_MHF_GET_UD_TACTICS_FIRST_QUEST_BONUS
MSG_MHF_GET_UD_TACTICS_REMAINING_POINT
MSG_SYS_reserve188
MSG_MHF_LOAD_PLATE_MYSET
MSG_MHF_SAVE_PLATE_MYSET
MSG_SYS_reserve18B
MSG_MHF_GET_RESTRICTION_EVENT
MSG_MHF_SET_RESTRICTION_EVENT
MSG_SYS_reserve18E
MSG_SYS_reserve18F
MSG_MHF_GET_TREND_WEAPON
MSG_MHF_UPDATE_USE_TREND_WEAPON_LOG
MSG_SYS_reserve192
MSG_SYS_reserve193
MSG_SYS_reserve194
MSG_MHF_SAVE_RENGOKU_DATA
MSG_MHF_LOAD_RENGOKU_DATA
MSG_MHF_GET_RENGOKU_BINARY
MSG_MHF_ENUMERATE_RENGOKU_RANKING
MSG_MHF_GET_RENGOKU_RANKING_RANK
MSG_MHF_ACQUIRE_EXCHANGE_SHOP
MSG_SYS_reserve19B
MSG_MHF_SAVE_MEZFES_DATA
MSG_MHF_LOAD_MEZFES_DATA
MSG_SYS_reserve19E
MSG_SYS_reserve19F
MSG_MHF_UPDATE_FORCE_GUILD_RANK
MSG_MHF_RESET_TITLE
MSG_SYS_reserve202
MSG_SYS_reserve203
MSG_SYS_reserve204
MSG_SYS_reserve205
MSG_SYS_reserve206
MSG_SYS_reserve207
MSG_SYS_reserve208
MSG_SYS_reserve209
MSG_SYS_reserve20A
MSG_SYS_reserve20B
MSG_SYS_reserve20C
MSG_SYS_reserve20D
MSG_SYS_reserve20E
MSG_SYS_reserve20F
)
//revive:enable

454
network/packetid_string.go Normal file

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,20 @@ import (
"github.com/Andoryuuta/byteframe"
)
/*
var conf *config.Config
func init() {
c, err := config.LoadConfig("config.toml")
if err != nil {
panic(err)
}
conf = c
}
*/
func paddedString(x string, size uint) []byte {
out := make([]byte, size)
copy(out, x)
@@ -31,6 +45,10 @@ func uint16PascalString(bf *byteframe.ByteFrame, x string) {
func makeSignInResp(username string) []byte {
bf := byteframe.NewByteFrame()
// delete me:
//bf.WriteUint8(8)
//return bf.Data()
bf.WriteUint8(1) // resp_code
bf.WriteUint8(0) // file/patch server count
bf.WriteUint8(1) // entrance server count
@@ -44,17 +62,65 @@ func makeSignInResp(username string) []byte {
// Array(this.entrance_server_count, PascalString(Byte, "utf8")),
uint8PascalString(bf, "localhost:53310")
///////////////////////////
// Characters:
bf.WriteUint32(1039336769) // character ID
bf.WriteUint16(30)
bf.WriteUint16(7)
bf.WriteUint32(1576761172)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteBytes(paddedString("username", 16))
bf.WriteBytes(paddedString("", 32))
/*
tab = '123456789ABCDEFGHJKLMNPQRTUVWXYZ'
def make_uid_str(cid):
out = ''
for i in range(6):
v = (cid>>5*i)
out += tab[v&0x1f]
return out
def make_cid_int(uid):
v = 0
for c in uid[::-1]:
idx = tab.find(c)
if idx == -1:
raise Exception("not in tab")
v |= idx
v = v<<5
return v>>5
*/
bf.WriteUint32(469153291) // character ID 469153291
bf.WriteUint16(30) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
/*
0=大劍/Big sword
1=重弩/Heavy crossbow
2=大錘/Sledgehammer
3=長槍/Spear
4=單手劍/One-handed sword
5=輕弩/Light crossbow
6=雙劍/Double sword
7=太刀/Tadao
8=狩獵笛/Hunting flute
9=銃槍/Shotgun
10=弓/bow
11=穿龍棍/Wear a dragon stick
12=斬擊斧F/Chopping Axe F
13=---
default=不明/unknown
*/
bf.WriteUint16(7) // Weapon, 0-13.
bf.WriteUint32(1576761172) // Last login date, unix timestamp in seconds.
bf.WriteUint8(1) // Sex, 0=male, 1=female.
bf.WriteUint8(0) // Is new character, 1 replaces character name with ?????.
grMode := uint8(0)
bf.WriteUint8(1) // GR level if grMode == 0
bf.WriteUint8(grMode) // GR mode.
bf.WriteBytes(paddedString(username, 16)) // Character name
bf.WriteBytes(paddedString("0", 32)) // unk str
if grMode == 1 {
bf.WriteUint16(55) // GR level override.
bf.WriteUint8(0) // unk
bf.WriteUint8(0) // unk
}
//////////////////////////
bf.WriteUint8(0) // friends_list_count
bf.WriteUint8(0) // guild_members_count

182
test.py
View File

@@ -1,4 +1,5 @@
from hexdump import hexdump
import io
import sys
ENCRYPT_KEY = b'\x90\x51\x26\x25\x04\xBF\xCF\x4C\x92\x02\x52\x7A\x70\x1A\x41\x88\x8C\xC2\xCE\xB8\xF6\x57\x7E\xBA\x83\x63\x2C\x24\x9A\x67\x86\x0C\xBE\x72\xFD\xB6\x7B\x79\xB0\x22\x5A\x60\x5C\x4F\x49\xE2\x0E\xF5\x3A\x81\xAE\x11\x6B\xF0\xA1\x01\xE8\x65\x8D\x5B\xDC\xCC\x93\x18\xB3\xAB\x77\xF7\x8E\xEC\xEF\x05\x00\xCA\x4E\xA7\xBC\xB5\x10\xC6\x6C\xC0\xC4\xE5\x87\x3F\xC1\x82\x29\x96\x45\x73\x07\xCB\x43\xF9\xF3\x08\x89\xD0\x99\x6A\x3B\x37\x19\xD4\x40\xEA\xD7\x85\x16\x66\x1E\x9C\x39\xBB\xEE\x4A\x03\x8A\x36\x2D\x13\x1D\x56\x48\xC7\x0D\x59\xB2\x44\xA3\xFE\x8B\x32\x1B\x84\xA0\x2E\x62\x17\x42\xB9\x9B\x2B\x75\xD8\x1C\x3C\x4D\x76\x27\x6E\x28\xD3\x33\xC3\x21\xAF\x34\x23\xDD\x68\x9F\xF1\xAD\xE1\xB4\xE7\xA6\x74\x15\x4B\xFA\x3D\x5F\x7C\xDA\x2F\x0A\xE3\x7D\xC8\xB7\x12\x6F\x9E\xA9\x14\x53\x97\x8F\x64\xF4\xF8\xA2\xA4\x2A\xD2\x47\x9D\x71\xC5\xE9\x06\x98\x20\x54\x80\xAA\xF2\xAC\x50\xD6\x7F\xD9\xC9\xCD\x69\x46\x6D\x30\xB1\x58\x0B\x55\xD1\x5D\xD5\xBD\x31\xDE\xA5\xE4\x91\x0F\x61\x38\xDF\xA8\xE6\x3E\x1F\x35\xED\xDB\x94\xEB\x09\x5E\x95\xFB\xFC\xE0\x78\xFF'
@@ -109,6 +110,42 @@ Binary8Header = Struct(
"checksum" / Int32ub,
)
EntranceListComplete = Struct(
Embedded(Binary8Header),
"servers" / Array(this.entry_count,
Struct(
"host_ip_4byte" / Int32ub,
"unk_1" / Int16ub, # Server ID maybe?
"unk_2" / Int16ub,
"channel_count" / Int16ub,
"unk_4" / Byte,
"unk_5" / Byte,
"unk_6" / Byte,
"name" / Bytes(66), # Shift-JIS.
"unk_trailer" / Int32ub, # THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
"channels" / Array(this.channel_count,
Struct(
"port" / Int16ub,
"unk_1" / Int16ub, # Channel ID maybe?
"max_players" / Int16ub,
"current_players" / Int16ub,
"unk_4" / Int16ub,
"unk_5" / Int16ub,
"unk_6" / Int16ub,
"unk_7" / Int16ub,
"unk_8" / Int16ub,
"unk_9" / Int16ub,
"unk_10" / Int16ub,
"unk_11" / Int16ub,
"unk_12" / Int16ub,
"unk_13" / Int16ub,
)
),
)
),
)
BINARY8_KEY = bytes([0x01, 0x23, 0x34, 0x45, 0x56, 0xAB, 0xCD, 0xEF])
def decode_binary8(data, unk_key_byte):
cur_key = ((54323 * unk_key_byte) + 1) & 0xFFFFFFFF
@@ -134,7 +171,24 @@ def encode_binary8(data, unk_key_byte):
SUM32_TABLE_0 = bytes([0x35, 0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12])
SUM32_TABLE_1 = bytes([0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12, 0xDE, 0xDE, 0x35])
# BROKEN!!!:
def calc_sum32(data):
length = len(data)
t0_i = length & 0xFF
t1_i = data[length >> 1]
out = bytearray(4)
for i in range(len(data)):
t0_i += 1
t1_i += 1
tmp = (SUM32_TABLE_1[t1_i % 9] ^ SUM32_TABLE_0[t0_i % 7]) ^ data[i]
out[i & 3] = (out[i & 3] + tmp) & 0xFF
return Int32ub.parse(out)
"""
# WARNING: Possibly broken?:
def calc_sum32(data):
t0_i = len(data)
t1_i = data[(len(data) >> 1)+1]
@@ -147,7 +201,8 @@ def calc_sum32(data):
t1_i += 1
return Int32ul.parse(out)
return Int32ub.parse(out)
"""
def read_binary8_part(stream):
# Read the header and decrypt the header first to get the size.
@@ -160,24 +215,121 @@ def read_binary8_part(stream):
enc_bytes.extend(body_bytes)
dec_bytes = decode_binary8(enc_bytes[1:], enc_bytes[0])
reenc_bytes = encode_binary8(dec_bytes, enc_bytes[0])
import zlib
print("Good: {}".format(zlib.crc32(enc_bytes[1:]) == zlib.crc32(reenc_bytes)))
print("calc_sum32: {:X}".format(calc_sum32(dec_bytes[11:])))
print("header checksum: {:X}".format(header.checksum))
# Then return the parsed header and just the raw body data.
return (header, dec_bytes[11:])
return (enc_bytes[0], header, dec_bytes[11:], dec_bytes)
def write_binary8_part(key, server_type, entry_count, payload):
body = Binary8Header.build(dict(
server_type=server_type,
entry_count=entry_count,
body_size=len(payload),
checksum=calc_sum32(payload),
))
temp = bytearray()
temp.extend(body)
temp.extend(payload)
out = bytearray()
out.append(key)
out.extend(encode_binary8(temp, key))
return out
def pad_bytes_to_len(b, length):
out = bytearray(b)
diff = length-len(out)
out.extend(bytearray(diff))
return bytes(out)
def make_custom_entrance_server_resp():
# Get the userinfo_data
with open('tw_server_list_resp.bin', 'rb') as f:
(key, header, data, raw_dec_bytes) = read_binary8_part(f)
userinfo_data = f.read()
data = EntranceListComplete.build(dict(
server_type = b'SV2',
entry_count = 1,
body_size = 0xFFFF,
checksum = 0xFFFFFFFF,
servers = [dict(
host_ip_4byte = 0x0100007F, #0x7F000001,#3377555739,
unk_1 = 16,
unk_2 = 0,
channel_count = 1,
unk_4 = 3,
unk_5 = 0,
unk_6 = 2,
name = pad_bytes_to_len("AErupe Server @localhost".encode('shift-jis'), 66),
unk_trailer = 0,
channels = [dict(
port = 54001,
unk_1 = 16,
max_players = 100,
current_players = 0,
unk_4 = 0,
unk_5 = 0,
unk_6 = 0,
unk_7 = 0,
unk_8 = 0,
unk_9 = 0,
unk_10 = 319,
unk_11 = 248,#254,
unk_12 = 159,#255,
unk_13 = 12345
)],
)]
))
print(data)
reencoded = write_binary8_part(0, b'SV2', 1, data[11:])
with open('custom_entrance_server_resp.bin', 'wb') as f:
f.write(reencoded)
f.write(userinfo_data)
with open('custom_entrance_server_resp.bin', 'rb') as f:
(key, header, data, raw_dec_bytes) = read_binary8_part(f)
print(EntranceListComplete.parse(raw_dec_bytes[0:]))
make_custom_entrance_server_resp()
"""
with open('tw_server_list_resp.bin', 'rb') as f:
(header, data) = read_binary8_part(f)
from hexdump import hexdump
hexdump(data[:16])
print(len(data))
(key, header, data, raw_dec_bytes) = read_binary8_part(f)
print(EntranceListComplete.parse(raw_dec_bytes[0:]))
"""
"""
with open('tw_server_list_resp.bin', 'rb') as f:
filedata = f.read()
rdr = io.BytesIO(filedata)
(key, header, data, raw_dec_bytes) = read_binary8_part(rdr)
userinfo_data = rdr.read()
reencoded = write_binary8_part(key, header.server_type, header.entry_count, data)
hexdump(reencoded[:16])
hexdump(filedata[:16])
"""
"""
with open('dec_bin8_data_dump.bin', 'rb') as f:
print("calc_sum32: {:X}".format(calc_sum32(f.read())))
print("want: 74EF4928")
print("want: 74EF4928")
"""