mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 23:14:36 +01:00
Fix client crash and implement quest loading
Fixed client crashes caused by handleMsgMhfEnumeratePrice and handleMsgMhfEnumerateRanking wherein the server's response didn't contain enough data, causing the client to read uninitalized memory. Implemented quest loading handlers GetFile, WaitStageBinary, and UnlockStage, as well as correcting the IssueLogkey handler.
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,11 +1,6 @@
|
||||
www/tw/
|
||||
www/jp/
|
||||
|
||||
bin_resp/
|
||||
bin/quests/*.bin
|
||||
savedata/
|
||||
custom_entrance_server_resp.bin
|
||||
dec_bin8_data_dump.bin
|
||||
entrance_resp_bin8_encrypted.bin
|
||||
tw_server_list_resp.bin
|
||||
Erupe.exe
|
||||
test.py
|
||||
Erupe.exe
|
||||
2
bin/quests/__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt
Normal file
2
bin/quests/__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
place your quest .bin files here
|
||||
e.g. 22031d0.bin
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"host_ip": "127.0.0.1",
|
||||
"bin_path": "bin",
|
||||
|
||||
"database": {
|
||||
"host": "localhost",
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
// Config holds the global server-wide config.
|
||||
type Config struct {
|
||||
HostIP string `mapstructure:"host_ip"`
|
||||
BinPath string `mapstructure:"bin_path"`
|
||||
|
||||
Database Database
|
||||
Launcher Launcher
|
||||
|
||||
@@ -5,8 +5,21 @@ import (
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
)
|
||||
|
||||
type scenarioFileIdentifer struct {
|
||||
Unk0 uint8
|
||||
Unk1 uint32
|
||||
Unk2 uint8
|
||||
Unk3 uint8
|
||||
}
|
||||
|
||||
// MsgSysGetFile represents the MSG_SYS_GET_FILE
|
||||
type MsgSysGetFile struct{}
|
||||
type MsgSysGetFile struct {
|
||||
AckHandle uint32
|
||||
IsScenario bool
|
||||
FilenameLength uint8
|
||||
Filename string
|
||||
ScenarioIdentifer scenarioFileIdentifer
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgSysGetFile) Opcode() network.PacketID {
|
||||
@@ -15,10 +28,23 @@ func (m *MsgSysGetFile) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgSysGetFile) Parse(bf *byteframe.ByteFrame) error {
|
||||
panic("Not implemented")
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.IsScenario = bf.ReadBool()
|
||||
if m.IsScenario {
|
||||
m.ScenarioIdentifer = scenarioFileIdentifer{
|
||||
bf.ReadUint8(),
|
||||
bf.ReadUint32(),
|
||||
bf.ReadUint8(),
|
||||
bf.ReadUint8(),
|
||||
}
|
||||
} else {
|
||||
m.FilenameLength = bf.ReadUint8()
|
||||
m.Filename = string(bf.ReadBytes(uint(m.FilenameLength)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a binary packet from the current data.
|
||||
func (m *MsgSysGetFile) Build(bf *byteframe.ByteFrame) error {
|
||||
panic("Not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
)
|
||||
|
||||
// MsgSysUnlockStage represents the MSG_SYS_UNLOCK_STAGE
|
||||
type MsgSysUnlockStage struct{}
|
||||
type MsgSysUnlockStage struct {
|
||||
Unk0 uint16 // Hardcoded 0 in the binary.
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgSysUnlockStage) Opcode() network.PacketID {
|
||||
@@ -15,7 +17,7 @@ func (m *MsgSysUnlockStage) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgSysUnlockStage) Parse(bf *byteframe.ByteFrame) error {
|
||||
// No data
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,14 @@ import (
|
||||
)
|
||||
|
||||
// MsgSysWaitStageBinary represents the MSG_SYS_WAIT_STAGE_BINARY
|
||||
type MsgSysWaitStageBinary struct{}
|
||||
type MsgSysWaitStageBinary struct{
|
||||
AckHandle uint32
|
||||
BinaryType0 uint8
|
||||
BinaryType1 uint8
|
||||
Unk0 uint32 // Hardcoded 0
|
||||
StageIDLength uint8
|
||||
StageID string
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgSysWaitStageBinary) Opcode() network.PacketID {
|
||||
@@ -15,7 +22,13 @@ func (m *MsgSysWaitStageBinary) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgSysWaitStageBinary) Parse(bf *byteframe.ByteFrame) error {
|
||||
panic("Not implemented")
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.BinaryType0 = bf.ReadUint8()
|
||||
m.BinaryType1 = bf.ReadUint8()
|
||||
m.Unk0 = bf.ReadUint32()
|
||||
m.StageIDLength = bf.ReadUint8()
|
||||
m.StageID = string(bf.ReadBytes(uint(m.StageIDLength)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a binary packet from the current data.
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -245,18 +246,35 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgSysCastedBinary(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysGetFile)
|
||||
|
||||
if !pkt.IsScenario {
|
||||
// Get quest file.
|
||||
data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", stripNullTerminator(pkt.Filename))))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
doSizedAckResp(s, pkt.AckHandle, data)
|
||||
} else {
|
||||
s.logger.Fatal("scenario getfile not implemented.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysIssueLogkey)
|
||||
|
||||
// Make a random log key for this session.
|
||||
logKey := make([]byte, 8)
|
||||
logKey := make([]byte, 16)
|
||||
_, err := rand.Read(logKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO(Andoryuuta): In the offical client, the log key index is off by one,
|
||||
// cutting off the last byte in _most uses_. Find and document these accordingly.
|
||||
s.Lock()
|
||||
s.logKey = logKey
|
||||
s.Unlock()
|
||||
@@ -264,7 +282,6 @@ func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) {
|
||||
// Issue it.
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteBytes(logKey)
|
||||
resp.WriteBytes([]byte{0x8E, 0x8E}) // Unk
|
||||
doSizedAckResp(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
@@ -425,7 +442,48 @@ func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysWaitStageBinary)
|
||||
defer s.logger.Debug("MsgSysWaitStageBinary Done!")
|
||||
|
||||
// Try to get the stage
|
||||
stageID := stripNullTerminator(pkt.StageID)
|
||||
s.server.stagesLock.Lock()
|
||||
stage, gotStage := s.server.stages[stageID]
|
||||
s.server.stagesLock.Unlock()
|
||||
|
||||
// If we got the stage, lock and try to get the data.
|
||||
var stageBinary []byte
|
||||
var gotBinary bool
|
||||
if gotStage {
|
||||
for {
|
||||
stage.Lock()
|
||||
stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
|
||||
stage.Unlock()
|
||||
|
||||
if gotBinary {
|
||||
doSizedAckResp(s, pkt.AckHandle, stageBinary)
|
||||
break
|
||||
} else {
|
||||
// Couldn't get binary, sleep for some time and try again.
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(Andoryuuta): Figure out what the game sends on timeout and implement it!
|
||||
/*
|
||||
if timeout {
|
||||
s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
|
||||
s.logger.Warn("Sending blank stage binary")
|
||||
doSizedAckResp(s, pkt.AckHandle, []byte{})
|
||||
return
|
||||
}
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
s.logger.Warn("Failed to get stage", zap.String("StageID", stageID))
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysSetStageBinary)
|
||||
@@ -495,14 +553,22 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
|
||||
// Read-lock the stage and make the response with all of the charID's in the stage.
|
||||
resp := byteframe.NewByteFrame()
|
||||
stage.RLock()
|
||||
resp.WriteUint16(uint16(len(stage.clients))) // Client count
|
||||
for session := range stage.clients {
|
||||
resp.WriteUint32(session.charID) // Client represented by charID
|
||||
|
||||
// TODO(Andoryuuta): Add proper player-slot reservations for stages.
|
||||
if len(stage.clients) >= 1 {
|
||||
resp.WriteUint16(uint16(len(stage.clients))) // Client count
|
||||
for session := range stage.clients {
|
||||
resp.WriteUint32(session.charID) // Client represented by charID
|
||||
}
|
||||
} else {
|
||||
// Just give our client.
|
||||
resp.WriteUint16(1)
|
||||
resp.WriteUint32(s.charID)
|
||||
}
|
||||
|
||||
stage.RUnlock()
|
||||
|
||||
doSizedAckResp(s, pkt.AckHandle, resp.Data())
|
||||
|
||||
s.logger.Debug("MsgSysEnumerateClient Done!")
|
||||
}
|
||||
|
||||
@@ -524,6 +590,7 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
doSizedAckResp(s, pkt.AckHandle, resp.Data())
|
||||
s.logger.Debug("handleMsgSysEnumerateStage Done!")
|
||||
}
|
||||
|
||||
func handleMsgSysCreateMutex(s *Session, p mhfpacket.MHFPacket) {}
|
||||
@@ -890,12 +957,28 @@ func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumeratePrice)
|
||||
stubEnumerateNoResults(s, pkt.AckHandle)
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint16(0) // Entry type 1 count
|
||||
resp.WriteUint16(0) // Entry type 2 count
|
||||
|
||||
doSizedAckResp(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateRanking)
|
||||
stubEnumerateNoResults(s, pkt.AckHandle)
|
||||
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint8(0)
|
||||
resp.WriteUint8(0) // Some string length following this field.
|
||||
resp.WriteUint16(0) // Entry type 1 count
|
||||
resp.WriteUint8(0) // Entry type 2 count
|
||||
|
||||
doSizedAckResp(s, pkt.AckHandle, resp.Data())
|
||||
|
||||
// Update the client's rights as well:
|
||||
updateRights(s)
|
||||
|
||||
Reference in New Issue
Block a user