-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReplay.cpp
More file actions
190 lines (150 loc) · 7.16 KB
/
Copy pathReplay.cpp
File metadata and controls
190 lines (150 loc) · 7.16 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include "amxxmodule.h"
#include "Replay.h"
#include <fstream>
#include <iostream>
#include <string>
#include "utils.h"
void Replay::encode(const std::string& output_filename)
{
std::ofstream output_file(output_filename, std::ios::binary);
if (!output_file.is_open()) {
std::cerr << "Error opening output file: " << output_filename << std::endl;
return;
}
std::vector<uint8_t> encoded_data = encodeHeader(header);
output_file.write(reinterpret_cast<const char*>(encoded_data.data()), encoded_data.size());
if (frames.size() < 1)
{
std::cerr<<"No Frames in replay!";
return;
}
encoded_data = frames[0].encode();
output_file.write(reinterpret_cast<const char*>(encoded_data.data()), encoded_data.size());
for (size_t i = 1; i < frames.size(); i++)
{
encoded_data = frames[i].encode_delta(frames[i - 1]);
output_file.write(reinterpret_cast<const char*>(encoded_data.data()), encoded_data.size());
}
}
Replay Replay::decode(const std::string& input_filename)
{
Replay replay;
std::ifstream input_file(input_filename, std::ios::binary);
if (!input_file.is_open()) {
std::cerr << "Error opening input file: " << input_filename << std::endl;
return replay;
}
std::vector<uint8_t> buffer((std::istreambuf_iterator<char>(input_file)),
std::istreambuf_iterator<char>());
input_file.close();
size_t offset = 133;
std::vector<uint8_t> header_data(buffer.begin(), buffer.begin() + offset);
replay.header = Replay::decodeHeader(header_data);
FrameData* prev_frame = nullptr;
while (offset < buffer.size()) {
// Create a vector for the current frame data starting from the current offset
std::vector<uint8_t> frame_data(buffer.begin() + offset, buffer.end());
// Decode the frame
int origin[3] = { 0, 0, 0 };
int angles[2] = { 0, 0 };
FrameData current_frame(0, origin, angles, 0, 0, 0, 0, 0, false, false);
// Update offset with the number of bytes processed in the current frame
offset += current_frame.decode(frame_data, prev_frame);
replay.addFrame(current_frame);
// Update prev_frame to point to the current frame (for delta decoding)
if (prev_frame != nullptr) {
delete prev_frame;
}
prev_frame = new FrameData(current_frame);
}
// Clean up
if (prev_frame != nullptr) {
delete prev_frame;
}
return replay;
}
void Replay::addFrame(const FrameData frame)
{
frames.push_back(frame);
}
std::vector<uint8_t> Replay::encodeHeader(const Header& header)
{
std::vector<uint8_t> packed_header;
// Encode timestamp (8 bytes)
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 56) & 0xFF));
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 48) & 0xFF));
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 40) & 0xFF));
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 32) & 0xFF));
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 24) & 0xFF));
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 16) & 0xFF));
packed_header.push_back(static_cast<uint8_t>((header.timestamp >> 8) & 0xFF));
packed_header.push_back(static_cast<uint8_t>(header.timestamp & 0xFF));
// Encode version (2 bytes)
packed_header.push_back(static_cast<uint8_t>((header.version >> 8) & 0xFF)); // High byte
packed_header.push_back(static_cast<uint8_t>(header.version & 0xFF)); // Low byte
// Encode map name (32 bytes, padded or truncated)
for (size_t i = 0; i < 32; ++i) {
packed_header.push_back(i < header.map.size() ? header.map[i] : '\0');
}
// Encode time (3 bytes)
packed_header.push_back(static_cast<uint8_t>((header.time >> 16) & 0xFF)); // MSB
packed_header.push_back(static_cast<uint8_t>((header.time >> 8) & 0xFF)); // Middle byte
packed_header.push_back(static_cast<uint8_t>(header.time & 0xFF)); // LSB
// Encode player name (32 bytes, padded or truncated)
for (size_t i = 0; i < 32; ++i) {
packed_header.push_back(i < header.name.size() ? header.name[i] : '\0');
}
// Encode SteamID (24 bytes, padded or truncated)
for (size_t i = 0; i < 24; ++i) {
packed_header.push_back(i < header.steamID.size() ? header.steamID[i] : '\0');
}
// Encode additional info (32 bytes, padded or truncated)
for (size_t i = 0; i < 32; ++i) {
packed_header.push_back(i < header.info.size() ? header.info[i] : '\0');
}
return packed_header;
}
Header Replay::decodeHeader(const std::vector<uint8_t>& packed_header) {
if (packed_header.size() < 133) {
throw std::invalid_argument("Header data is incomplete or corrupted.");
}
Header header;
size_t offset = 0;
// Decode timestamp (8 bytes)
header.timestamp =
(static_cast<uint64_t>(packed_header[offset]) << 56) | // Byte 1 (Most significant byte)
(static_cast<uint64_t>(packed_header[offset + 1]) << 48) | // Byte 2
(static_cast<uint64_t>(packed_header[offset + 2]) << 40) | // Byte 3
(static_cast<uint64_t>(packed_header[offset + 3]) << 32) | // Byte 4
(static_cast<uint64_t>(packed_header[offset + 4]) << 24) | // Byte 5
(static_cast<uint64_t>(packed_header[offset + 5]) << 16) | // Byte 6
(static_cast<uint64_t>(packed_header[offset + 6]) << 8) | // Byte 7
static_cast<uint64_t>(packed_header[offset + 7]); // Byte 8 (Least significant byte)
offset += 8; // Move to the next field after reading 8 bytes
// Decode version (2 bytes)
header.version = (static_cast<uint16_t>(packed_header[offset]) << 8) |
static_cast<uint16_t>(packed_header[offset + 1]);
offset += 2;
// Decode map name (32 bytes, trim null padding)
header.map = std::string(packed_header.begin() + offset, packed_header.begin() + offset + 32);
header.map = header.map.substr(0, header.map.find('\0'));
offset += 32;
// Decode time (3 bytes)
header.time = (static_cast<uint16_t>(packed_header[offset]) << 16) |
(static_cast<uint16_t>(packed_header[offset + 1]) << 8) |
static_cast<uint16_t>(packed_header[offset + 2]);
offset += 3;
// Decode player name (32 bytes, trim null padding)
header.name = std::string(packed_header.begin() + offset, packed_header.begin() + offset + 32);
header.name = header.name.substr(0, header.name.find('\0'));
offset += 32;
// Decode SteamID (24 bytes, trim null padding)
header.steamID = std::string(packed_header.begin() + offset, packed_header.begin() + offset + 24);
header.steamID = header.steamID.substr(0, header.steamID.find('\0'));
offset += 24;
// Decode additional info (32 bytes, trim null padding)
header.info = std::string(packed_header.begin() + offset, packed_header.begin() + offset + 32);
header.info = header.info.substr(0, header.info.find('\0'));
offset += 32;
return header;
}