-
Notifications
You must be signed in to change notification settings - Fork 16
/
FileRequestService.cs
120 lines (98 loc) · 4.66 KB
/
FileRequestService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using AutomaticTypeMapper;
using EOLib.Domain.Protocol;
using EOLib.IO.Map;
using EOLib.IO.Pub;
using EOLib.IO.Services.Serializers;
using EOLib.Net.Communication;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace EOLib.Net.FileTransfer
{
[AutoMappedType]
public class FileRequestService : IFileRequestService
{
private readonly IPacketSendService _packetSendService;
private readonly IMapDeserializer<IMapFile> _mapFileSerializer;
private readonly IPubFileDeserializer _pubFileDeserializer;
public FileRequestService(IPacketSendService packetSendService,
IMapDeserializer<IMapFile> mapFileSerializer,
IPubFileDeserializer pubFileDeserializer)
{
_packetSendService = packetSendService;
_mapFileSerializer = mapFileSerializer;
_pubFileDeserializer = pubFileDeserializer;
}
public async Task<IMapFile> RequestMapFile(short mapID, short sessionID)
{
var request = new PacketBuilder(PacketFamily.Welcome, PacketAction.Agree)
.AddChar((byte)InitFileType.Map)
.AddShort((ushort)sessionID)
.AddShort(mapID)
.Build();
return await GetMapFile(request, mapID);
}
public async Task<IMapFile> RequestMapFileForWarp(short mapID, short sessionID)
{
var request = new PacketBuilder(PacketFamily.Warp, PacketAction.Take)
.AddShort(mapID)
.AddShort((ushort)sessionID)
.Build();
return await GetMapFile(request, mapID);
}
public async Task<IPubFile<TRecord>> RequestFile<TRecord>(InitFileType fileType, short sessionID)
where TRecord : class, IPubRecord, new()
{
var request = new PacketBuilder(PacketFamily.Welcome, PacketAction.Agree)
.AddChar((byte)fileType)
.AddShort((ushort)sessionID)
.AddChar(1) // file id (for chunking oversize pub files)
.Build();
var response = await _packetSendService.SendEncodedPacketAndWaitAsync(request);
if (!PacketIsValid(response))
throw new EmptyPacketReceivedException();
var responseFileType = (InitReply) response.ReadByte();
var extraByte = response.ReadChar();
if (extraByte != 1)
throw new MalformedPacketException("Missing extra single byte in file transfer packet", response);
Func<IPubFile<TRecord>> factory;
switch (responseFileType)
{
case InitReply.ItemFile: factory = () => (IPubFile<TRecord>)new EIFFile(); break;
case InitReply.NpcFile: factory = () => (IPubFile<TRecord>)new ENFFile(); break;
case InitReply.SpellFile: factory = () => (IPubFile<TRecord>)new ESFFile(); break;
case InitReply.ClassFile: factory = () => (IPubFile<TRecord>)new ECFFile(); break;
default: throw new EmptyPacketReceivedException();
}
var responseBytes = response
.ReadBytes(response.Length - response.ReadPosition)
.ToArray();
return _pubFileDeserializer.DeserializeFromByteArray(responseBytes, factory);
}
private async Task<IMapFile> GetMapFile(IPacket request, int mapID)
{
var response = await _packetSendService.SendEncodedPacketAndWaitAsync(request);
if (!PacketIsValid(response))
throw new EmptyPacketReceivedException();
var fileType = (InitReply)response.ReadByte();
if (fileType != InitReply.MapFile && fileType != InitReply.WarpMap)
throw new MalformedPacketException("Invalid file type " + fileType + " when requesting a map file", response);
var fileData = response.ReadBytes(response.Length - response.ReadPosition);
var mapFile = _mapFileSerializer
.DeserializeFromByteArray(fileData.ToArray())
.WithMapID(mapID);
return mapFile;
}
private static bool PacketIsValid(IPacket packet)
{
return packet.Family == PacketFamily.Init && packet.Action == PacketAction.Init;
}
}
public interface IFileRequestService
{
Task<IMapFile> RequestMapFile(short mapID, short sessionID);
Task<IMapFile> RequestMapFileForWarp(short mapID, short sessionID);
Task<IPubFile<TRecord>> RequestFile<TRecord>(InitFileType fileType, short sessionID)
where TRecord : class, IPubRecord, new();
}
}