Lua: Use multiarg for rest of gui-* stuff
[lsnes.git] / src / video / raw.cpp
blob0dad626b73f370f9a7325ba654f0c0dd65e428e8
1 #include "core/advdumper.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/moviefile.hpp"
5 #include "video/tcp.hpp"
6 #include "library/serialization.hpp"
8 #include <iomanip>
9 #include <cassert>
10 #include <cstring>
11 #include <cerrno>
12 #include <cstring>
13 #include <sstream>
14 #include <fstream>
15 #include <zlib.h>
17 #define IS_RGB(m) (((m) + ((m) >> 3)) & 2)
18 #define IS_64(m) (m % 5 < 2)
19 #define IS_TCP(m) (((m % 5) * (m % 5)) % 5 == 1)
21 namespace
23 uint64_t akill = 0;
24 double akillfrac = 0;
26 unsigned strhash(const std::string& str)
28 unsigned h = 0;
29 for(size_t i = 0; i < str.length(); i++)
30 h = (2 * h + static_cast<unsigned char>(str[i])) % 11;
31 return h;
34 void deleter_fn(void* f)
36 delete reinterpret_cast<std::ofstream*>(f);
39 class raw_avsnoop : public information_dispatch
41 public:
42 raw_avsnoop(const std::string& prefix, bool _swap, bool _bits64, bool socket_mode)
43 : information_dispatch("dump-raw")
45 enable_send_sound();
46 if(socket_mode) {
47 socket_address videoaddr = socket_address(prefix);
48 socket_address audioaddr = videoaddr.next();
49 deleter = socket_address::deleter();
50 video = audio = NULL;
51 try {
52 video = &videoaddr.connect();
53 audio = &audioaddr.connect();
54 } catch(...) {
55 deleter(video);
56 deleter(audio);
57 throw;
59 } else {
60 video = new std::ofstream(prefix + ".video", std::ios::out | std::ios::binary);
61 audio = new std::ofstream(prefix + ".audio", std::ios::out | std::ios::binary);
62 deleter = deleter_fn;
64 if(!*video || !*audio)
65 throw std::runtime_error("Can't open output files");
66 have_dumped_frame = false;
67 swap = _swap;
68 bits64 = _bits64;
71 ~raw_avsnoop() throw()
73 if(video)
74 deleter(video);
75 if(audio)
76 deleter(audio);
79 void on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
81 if(!video)
82 return;
83 unsigned magic;
84 if(bits64)
85 magic = 0x30201000U;
86 else
87 magic = 0x18100800U;
88 unsigned r = (reinterpret_cast<unsigned char*>(&magic))[swap ? 2 : 0];
89 unsigned g = (reinterpret_cast<unsigned char*>(&magic))[1];
90 unsigned b = (reinterpret_cast<unsigned char*>(&magic))[swap ? 0 : 2];
91 auto scl = our_rom.rtype->get_scale_factors(_frame.get_width(), _frame.get_height());
92 uint32_t hscl = scl.first;
93 uint32_t vscl = scl.second;
94 if(bits64) {
95 size_t w = dscr2.get_width();
96 size_t h = dscr2.get_height();
97 if(!render_video_hud(dscr2, _frame, hscl, vscl, r, g, b, 0, 0, 0, 0, NULL)) {
98 akill += killed_audio_length(fps_n, fps_d, akillfrac);
99 return;
101 for(size_t i = 0; i < h; i++)
102 video->write(reinterpret_cast<char*>(dscr2.rowptr(i)), 8 * w);
103 } else {
104 size_t w = dscr.get_width();
105 size_t h = dscr.get_height();
106 if(!render_video_hud(dscr, _frame, hscl, vscl, r, g, b, 0, 0, 0, 0, NULL)) {
107 akill += killed_audio_length(fps_n, fps_d, akillfrac);
108 return;
110 for(size_t i = 0; i < h; i++)
111 video->write(reinterpret_cast<char*>(dscr.rowptr(i)), 4 * w);
113 if(!*video)
114 messages << "Video write error" << std::endl;
115 have_dumped_frame = true;
118 void on_sample(short l, short r)
120 if(akill) {
121 akill--;
122 return;
124 if(have_dumped_frame && audio) {
125 char buffer[4];
126 serialization::s16b(buffer + 0, l);
127 serialization::s16b(buffer + 2, r);
128 audio->write(buffer, 4);
132 void on_dump_end()
134 deleter(video);
135 deleter(audio);
136 video = NULL;
137 audio = NULL;
140 bool get_dumper_flag() throw()
142 return true;
144 private:
145 std::ostream* audio;
146 std::ostream* video;
147 void (*deleter)(void* f);
148 bool have_dumped_frame;
149 struct framebuffer::fb<false> dscr;
150 struct framebuffer::fb<true> dscr2;
151 bool swap;
152 bool bits64;
155 raw_avsnoop* vid_dumper;
157 class adv_raw_dumper : public adv_dumper
159 public:
160 adv_raw_dumper() : adv_dumper("INTERNAL-RAW") {information_dispatch::do_dumper_update(); }
161 ~adv_raw_dumper() throw();
162 std::set<std::string> list_submodes() throw(std::bad_alloc)
164 std::set<std::string> x;
165 for(size_t i = 0; i < (socket_address::supported() ? 2 : 1); i++)
166 for(size_t j = 0; j < 2; j++)
167 for(size_t k = 0; k < 2; k++)
168 x.insert(std::string("") + (i ? "tcp" : "") + (j ? "bgr" : "rgb")
169 + (k ? "64" : "32"));
170 return x;
173 unsigned mode_details(const std::string& mode) throw()
175 return IS_TCP(strhash(mode)) ? target_type_special : target_type_prefix;
178 std::string mode_extension(const std::string& mode) throw()
180 return ""; //Nothing interesting.
183 std::string name() throw(std::bad_alloc)
185 return "RAW";
188 std::string modename(const std::string& mode) throw(std::bad_alloc)
190 unsigned _mode = strhash(mode);
191 std::string x = std::string((IS_RGB(_mode) ? "RGB" : "BGR")) +
192 (IS_64(_mode) ? " 64-bit" : " 32-bit") + (IS_TCP(_mode) ? " over TCP/IP" : "");
193 return x;
196 bool busy()
198 return (vid_dumper != NULL);
201 void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc,
202 std::runtime_error)
204 unsigned _mode = strhash(mode);
205 bool bits64 = IS_64(_mode);
206 bool swap = !IS_RGB(_mode);
207 bool sock = IS_TCP(_mode);
209 if(prefix == "")
210 throw std::runtime_error("Expected prefix");
211 if(vid_dumper)
212 throw std::runtime_error("RAW dumping already in progress");
213 try {
214 vid_dumper = new raw_avsnoop(prefix, swap, bits64, sock);
215 } catch(std::bad_alloc& e) {
216 throw;
217 } catch(std::exception& e) {
218 std::ostringstream x;
219 x << "Error starting RAW dump: " << e.what();
220 throw std::runtime_error(x.str());
222 messages << "Dumping to " << prefix << std::endl;
223 information_dispatch::do_dumper_update();
224 akill = 0;
225 akillfrac = 0;
228 void end() throw()
230 if(!vid_dumper)
231 throw std::runtime_error("No RAW video dump in progress");
232 try {
233 vid_dumper->on_dump_end();
234 messages << "RAW Dump finished" << std::endl;
235 } catch(std::bad_alloc& e) {
236 throw;
237 } catch(std::exception& e) {
238 messages << "Error ending RAW dump: " << e.what() << std::endl;
240 delete vid_dumper;
241 vid_dumper = NULL;
242 information_dispatch::do_dumper_update();
244 } adv;
246 adv_raw_dumper::~adv_raw_dumper() throw()