10#include <simgear/io/iostreams/zlibstream.hxx>
11#include <simgear/props/props_io.hxx>
12#include <simgear/structure/commands.hxx>
14#include <osgViewer/ViewerBase>
24 SGPropertyNode* record_continuous =
fgGetNode(
"/sim/replay/record-continuous",
true);
25 SGPropertyNode* fdm_initialized =
fgGetNode(
"/sim/signals/fdm-initialized",
true);
26 record_continuous->addChangeListener(
this,
true );
27 fdm_initialized->addChangeListener(
this,
true );
32static void readRaw(std::istream& in, T& data)
34 in.read(
reinterpret_cast<char*
>(&data),
sizeof(data));
39static void writeRaw(std::ostream& out,
const T& data)
41 out.write(
reinterpret_cast<const char*
>(&data),
sizeof(data));
46template<
typename SizeType>
47static SizeType
VectorRead(std::istream& in, std::vector<char>& out, uint32_t max_length=(1u << 31))
51 if (
sizeof(length) + length > max_length)
53 SG_LOG(SG_SYSTEMS, SG_ALERT,
"recording data vector too long."
54 <<
" max_length=" << max_length
55 <<
" sizeof(length)=" <<
sizeof(length)
56 <<
" length=" << length
58 throw std::runtime_error(
"Failed to read vector in recording");
61 in.read(&out.front(), length);
62 return sizeof(length) + length;
75 std::vector<char> path(length);
76 in.read(&path[0], length);
78 std::string ret(&path[0], length);
84 std::stringstream buffer;
85 writeProperties(buffer, root,
true );
86 uint32_t buffer_len = buffer.str().size() + 1;
88 out.write(buffer.str().c_str(), buffer_len);
96 SG_LOG(SG_SYSTEMS, SG_BULK,
"reading extra-properties. length=" << length);
106 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Overrun while reading extra-properties:"
107 " length=" << length <<
": pos=" << pos);
108 in.setstate(std::ios_base::failbit);
111 SG_LOG(SG_SYSTEMS, SG_BULK,
"length=" << length<<
" pos=" << pos);
116 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"property deleted: " << path);
122 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"property changed: " << path <<
"=" << value);
130 SGPropertyNode* config,
132 bool load_multiplayer,
133 bool load_extra_properties,
138 for (
auto data: config->getChildren(
"data"))
140 std::string data_type = data->getStringValue();
141 SG_LOG(SG_SYSTEMS, SG_BULK,
"in.tellg()=" << in.tellg() <<
" data_type=" << data_type);
144 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"length=" << length);
146 if (load_signals && data_type ==
"signals")
151 else if (load_multiplayer && data_type ==
"multiplayer")
158 assert(pos <= length);
159 if (pos == length)
break;
160 std::shared_ptr<std::vector<char>> v(
new std::vector<char>);
163 SG_LOG(SG_SYSTEMS, SG_BULK,
"replaying multiplayer data"
164 <<
" ret->sim_time=" << ret->
sim_time
165 <<
" length=" << length
167 <<
" callsign=" << ((
T_MsgHdr*) &v->front())->Callsign
171 else if (load_extra_properties && data_type ==
"extra-properties")
177 SG_LOG(SG_GENERAL, SG_BULK,
"Skipping unrecognised/unwanted data: " << data_type);
178 in.seekg(length, std::ios_base::cur);
184 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Failed to read fgtape data");
191template<
typename Container,
typename Iterator>
194 SG_LOG(SG_GENERAL, SG_DEBUG,
"container.size()=" << container.size());
197 for (
int i=0;
i<n; ++
i)
199 if (it == container.end())
return;
202 container.erase(it, container.end());
206 for (
int i=0;
i<-n-1; ++
i)
208 if (it == container.begin())
return;
211 container.erase(container.begin(), it);
213 SG_LOG(SG_GENERAL, SG_DEBUG,
"container.size()=" << container.size());
223 SGPropertyNode* config,
225 bool load_multiplayer,
226 bool load_extra_properties,
230 std::shared_ptr<FGReplayData> ret;
236 || (load_signals && !it->second->load_signals)
237 || (load_multiplayer && !it->second->load_multiplayer)
238 || (load_extra_properties && !it->second->load_extra_properties)
255 SG_LOG(SG_SYSTEMS, SG_BULK,
"reading frame. pos=" << pos);
264 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Failed to read fgtape frame at offset " << pos);
271 uint32_t compressed_size;
272 in.read((
char*) &flags,
sizeof(flags));
273 in.read((
char*) &compressed_size,
sizeof(compressed_size));
274 simgear::ZlibDecompressorIStream in_decompress(in, SGPath(), simgear::ZLibCompressionFormat::ZLIB_RAW);
275 ok =
ReadFGReplayData2(in_decompress, config, load_signals, load_multiplayer, load_extra_properties, ret.get());
279 ok =
ReadFGReplayData2(in, config, load_signals, load_multiplayer, load_extra_properties, ret.get());
283 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Failed to read fgtape frame at offset " << pos);
292 size_t size_max = 2*n - 1;
296 SG_LOG(SG_GENERAL, SG_DEBUG,
""
298 <<
" size_max=" << size_max
299 <<
" size_old=" << size_old
300 <<
" size_new=" << size_new
302 assert(size_new <= size_max);
338 int e = deflateInit2(
340 Z_DEFAULT_COMPRESSION,
348 throw std::runtime_error(
"deflateInit2() failed");
374 if (!flush && !
zstream.avail_in)
break;
376 int e = deflate(&
zstream, (!
zstream.avail_in && flush) ? Z_FINISH : Z_NO_FLUSH);
377 if (e != Z_OK && e != Z_STREAM_END)
379 throw std::runtime_error(
"zip_deflate() failed");
381 if (e == Z_STREAM_END)
break;
386 if (!
out)
return true;
394 *this->pptr() = (char) c;
425 size_t buffer_uncompressed_size,
426 size_t buffer_compressed_size
430 streambuf(out, buffer_uncompressed_size, buffer_compressed_size)
440 for (
auto data: config->getChildren(
"data"))
442 std::string data_type = data->getStringValue();
443 if (data_type ==
"signals")
445 uint32_t signals_size = r->
raw_data.size();
449 else if (data_type ==
"multiplayer")
454 length +=
sizeof(uint16_t) + message->size();
456 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"data_type=" << data_type <<
" out.tellp()=" << out.tellp()
457 <<
" length=" << length);
461 uint16_t message_size = message->size();
463 out.write(&message->front(), message_size);
466 else if (data_type ==
"extra-properties")
469 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"data_type=" << data_type <<
" out.tellp()=" << out.tellp()
470 <<
" length=" << length);
476 SG_LOG(SG_SYSTEMS, SG_ALERT,
"unrecognised data_type=" << data_type);
487 SGPropertyNode_ptr config,
491 SG_LOG(SG_SYSTEMS, SG_BULK,
"writing frame."
492 <<
" out.tellp()=" << out.tellp()
497 bool has_signals =
false;
498 bool has_multiplayer =
false;
499 bool has_extra_properties =
false;
500 for (
auto data: config->getChildren(
"data"))
502 std::string data_type = data->getStringValue();
503 if (data_type ==
"signals")
507 else if (data_type ==
"multiplayer")
511 has_multiplayer =
true;
514 else if (data_type ==
"extra-properties")
518 has_extra_properties =
true;
523 SG_LOG(SG_SYSTEMS, SG_ALERT,
"unrecognised data_type=" << data_type);
527 if (!has_signals && !has_multiplayer && !has_extra_properties)
529 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Not writing frame because no data to write");
538 if (has_signals) flags |= 1;
539 if (has_multiplayer) flags |= 2;
540 if (has_extra_properties) flags |= 4;
541 out.write((
char*) &flags,
sizeof(flags));
545 std::ostringstream compressed;
548 out_compressing.flush();
550 uint32_t compressed_size = compressed.str().size();
551 out.write((
char*) &compressed_size,
sizeof(compressed_size));
552 out.write((
char*) compressed.str().c_str(), compressed.str().size());
559 if (!out) ok =
false;
572 SGPropertyNode_ptr config =
saveSetup(NULL , path, 0 ,
574 SGPropertyNode* signals = config->getNode(
"signals",
true );
577 out.open(path.c_str(), std::ofstream::binary | std::ofstream::trunc);
613 bool replay_multiplayer,
614 bool replay_extra_properties,
628 replay_extra_properties,
633 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Failed to read fgtape frame at offset=" << offset <<
" time=" << time);
636 assert(replay_data.get());
637 std::shared_ptr<FGReplayData> replay_data_old;
647 replay_extra_properties,
651 if (replay_extra_properties) SG_LOG(SG_SYSTEMS, SG_DEBUG,
654 <<
" offset=" << offset
655 <<
" offset_old=" << offset_old
656 <<
" replay_data_old=" << replay_data_old
657 <<
" replay_data->raw_data.size()=" << replay_data->raw_data.size()
658 <<
" replay_data->multiplayer_messages.size()=" << replay_data->multiplayer_messages.size()
659 <<
" replay_data->extra_properties.size()=" << replay_data->extra_properties.size()
660 <<
" replay_data->replay_extra_property_changes.size()=" << replay_data->replay_extra_property_changes.size()
662 recorder->
replay(time, replay_data.get(), replay_data_old.get(), xpos, ypos, xsize, ysize);
667static void popupTip(
const char* message,
int delay)
669 SGPropertyNode_ptr args(
new SGPropertyNode);
670 args->setStringValue(
"label", message);
671 args->setIntValue(
"delay", delay);
672 globals->get_commands()->execute(
"show-message", args);
679 SG_LOG(SG_GENERAL, SG_ALERT,
"Stopping replay create-video");
683 view_mgr->video_stop();
709 double multiplayer_recent = 3;
715 double t_begin = self.
m_continuous->m_in_frame_time_last;
717 if (time < self.m_continuous->m_in_time_last)
728 t_begin = time - multiplayer_recent;
739 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Have gone backwards."
740 <<
" m_in_time_last=" << self.
m_continuous->m_in_time_last
742 <<
" t_begin=" << t_begin
743 <<
" m_in_extra_properties=" << self.
m_continuous->m_in_extra_properties
751 auto p = self.
m_continuous->m_in_time_to_frameinfo.lower_bound(time);
755 size_t offset_prev = 0;
762 offset =
p->second.offset;
765 else if (
p->first > time)
771 offset =
p->second.offset;
778 offset_prev = prev->second.offset;
779 offset =
p->second.offset;
785 offset =
p->second.offset;
797 for (
auto p_before = self.
m_continuous->m_in_time_to_frameinfo.upper_bound(t_begin);
798 p_before != self.
m_continuous->m_in_time_to_frameinfo.end();
801 if (p_before->first >=
p->first)
810 bool replay_this_frame = p_before->second.has_extra_properties;
811 if (p_before->second.has_multiplayer && p_before->first > time - multiplayer_recent)
813 replay_this_frame =
true;
816 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Looking at extra property changes."
817 <<
" replay_this_frame=" << replay_this_frame
818 <<
" m_continuous->m_in_time_last=" << self.
m_continuous->m_in_time_last
819 <<
" m_continuous->m_in_frame_time_last=" << self.
m_continuous->m_in_frame_time_last
821 <<
" t_begin=" << t_begin
822 <<
" p_before->first=" << p_before->first
823 <<
" p_before->second=" << p_before->second
826 if (replay_this_frame)
829 if (p_before != self.
m_continuous->m_in_time_to_frameinfo.begin())
831 auto p_before_prev = p_before;
833 pos_prev = p_before_prev->second.offset;
839 p_before->second.offset,
842 p_before->first > time - multiplayer_recent ,
853 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Replay failed: cannot read fgtape data");
854 popupTip(
"Replay failed: cannot read fgtape data", 10);
882 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Replay failed: cannot read fgtape data");
883 popupTip(
"Replay failed: cannot read fgtape data", 10);
896 globals->get_props()->setIntValue(
"/sim/startup/xpos", xpos);
897 globals->get_props()->setIntValue(
"/sim/startup/ypos", ypos);
898 globals->get_props()->setIntValue(
"/sim/startup/xsize", xsize);
899 globals->get_props()->setIntValue(
"/sim/startup/ysize", ysize);
901 osgViewer::ViewerBase* viewer_base =
globals->get_renderer()->getViewerBase();
904 std::vector<osgViewer::GraphicsWindow*> windows;
905 viewer_base->getWindows(windows);
906 osgViewer::GraphicsWindow* window = windows[0];
927 bool prop_continuous =
fgGetBool(
"/sim/replay/record-continuous");
928 bool prop_fdm =
fgGetBool(
"/sim/signals/fdm-initialized");
930 bool continuous = prop_continuous && prop_fdm;
931 if (continuous == (
m_out.is_open() ?
true :
false))
940 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Stopping continuous recording");
942 popupTip(
"Continuous record to file stopped", 5 );
948 SGPath path_timeless;
959 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Failed to start continuous recording");
960 popupTip(
"Continuous record to file failed to start", 5 );
964 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Starting continuous recording");
971 path_timeless.remove();
972 bool ok = path_timeless.makeLink(path.file());
975 SG_LOG(SG_SYSTEMS, SG_ALERT,
"Failed to create link " << path_timeless.c_str() <<
" => " << path.file());
977 SG_LOG(SG_SYSTEMS, SG_DEBUG,
"Starting continuous recording to " << path);
980 popupTip(
"Continuous+compressed record to file started", 5 );
984 popupTip(
"Continuous record to file started", 5 );
void getConfig(SGPropertyNode *root)
void replay(double SimTime, const FGReplayData *pNextBuffer, const FGReplayData *pLastBuffer, int *main_window_xpos, int *main_window_ypos, int *main_window_xsize, int *main_window_ysize)
Replay.
void resetExtraProperties()
void setWindowRectangleInteriorWithCorrection(osgViewer::GraphicsWindow *window, int x, int y, int width, int height)
static int16_t read_int16(std::istream &in, size_t &pos)
bool continuousWriteFrame(Continuous &continuous, FGReplayData *r, std::ostream &out, SGPropertyNode_ptr config, FGTapeType tape_type)
static void writeFrame2(FGReplayData *r, std::ostream &out, SGPropertyNode_ptr config)
static std::string read_string(std::istream &in, size_t &pos)
static bool replayContinuousInternal(Continuous &continuous, FGFlightRecorder *recorder, double time, size_t offset, size_t offset_old, bool replay_signals, bool replay_multiplayer, bool replay_extra_properties, int *xpos, int *ypos, int *xsize, int *ysize)
static void ReadFGReplayDataExtraProperties(std::istream &in, FGReplayData *replay_data, uint32_t length)
static SizeType VectorRead(std::istream &in, std::vector< char > &out, uint32_t max_length=(1u<< 31))
static void remove_far_away(Container &container, Iterator it, int n)
bool replayContinuous(FGReplayInternal &self, double time)
static bool ReadFGReplayData2(std::istream &in, SGPropertyNode *config, bool load_signals, bool load_multiplayer, bool load_extra_properties, FGReplayData *ret)
static std::shared_ptr< FGReplayData > ReadFGReplayData(Continuous &continuous, std::ifstream &in, size_t pos, SGPropertyNode *config, bool load_signals, bool load_multiplayer, bool load_extra_properties, int in_compression)
void continuous_replay_video_end(Continuous &continuous)
static void writeRaw(std::ostream &out, const T &data)
SGPropertyNode_ptr continuousWriteHeader(Continuous &continuous, FGFlightRecorder *flight_recorder, std::ofstream &out, const SGPath &path, FGTapeType tape_type)
static void readRaw(std::istream &in, T &data)
static int PropertiesWrite(SGPropertyNode *root, std::ostream &out)
static void popupTip(const char *message, int delay)
SGPropertyNode_ptr continuousWriteHeader(Continuous &continuous, FGFlightRecorder *m_pRecorder, std::ofstream &out, const SGPath &path, FGTapeType tape_type)
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
const char *const FlightRecorderFileMagic
Magic string to verify valid FG flight recorder tapes.
SGPath makeSavePath(FGTapeType type, SGPath *path_timeless)
SGPropertyNode_ptr saveSetup(const SGPropertyNode *extra, const SGPath &path, double duration, FGTapeType tape_type, int continuous_compression)
double m_replay_fixed_dt_prev
Continuous(std::shared_ptr< FGFlightRecorder > flight_recorder)
SGPropertyNode_ptr m_out_config
std::map< size_t, std::shared_ptr< FGReplayData > > m_in_pos_to_frame
std::shared_ptr< FGFlightRecorder > m_flight_recorder
SGPropertyNode_ptr m_in_config
bool m_replay_create_video
void valueChanged(SGPropertyNode *node) override
std::vector< std::shared_ptr< std::vector< char > > > multiplayer_messages
std::vector< char > extra_properties
std::map< std::string, std::string > replay_extra_property_changes
std::vector< std::string > replay_extra_property_removals
std::vector< char > raw_data
SGPropertyNode_ptr m_sim_startup_xpos
std::unique_ptr< struct Continuous > m_continuous
std::shared_ptr< FGFlightRecorder > m_flight_recorder
SGPropertyNode_ptr m_replay_error
compression_streambuf streambuf
compression_ostream(std::ostream &out, size_t buffer_uncompressed_size, size_t buffer_compressed_size)
compression_streambuf(std::ostream &out, size_t buffer_uncompressed_size, size_t buffer_compressed_size)
std::unique_ptr< char[]> buffer_uncompressed
std::unique_ptr< char[]> buffer_compressed
bool _deflate(size_t n, bool flush)
size_t buffer_uncompressed_size
size_t buffer_compressed_size
int overflow(int c) override